Взаимодействие с железкой

Тема в разделе "WASM.ELECTRONICS", создана пользователем misha_irpen, 19 мар 2008.

  1. misha_irpen

    misha_irpen New Member

    Публикаций:
    0
    Регистрация:
    19 мар 2008
    Сообщения:
    6
    Есть некая полусамопальная PCI-железка, занимается формированием изображения на монохромной светодиодной матрице (думаю все видели такие в виде "бегущей строки"). У этой железки есть отображаемая в адресное пространство процессора память, содержащая битовую карту изображения. Мне нужно в эту память писать для формирования картинки. Сам процесс записи в общем ничего интересного не представляет, но вот куда писать -- непонятно.

    Все под DOS. Для начала я перебираю все шины в поиках этой железки (использую int 1Ah), а потом извлекаю ее свойства. Вот тут у меня три непонятки:
    1. От кого зависит номер BAR, в котором лежит нужный мне адрес? Может ли быть так, что на одной машине он один, а на другой будет уже другим?
    2. Как мне узнать размер отображаемого куска? Железки бывают разных модификаций с разными объемами памяти, а мне нужно стобы все было универсально. Ведь в Windows в свойствах любой железки можно увидеть полные диапазоны памяти, значит это возможно. А в BAR-ах есть только начало.
    3. Глазами нужный BAR я вижу и при попытке его использования все работает, но на некоторых системах вместо реального F0000000h (например) там почему-то лежит что-то вроде F0000008h. Причем это "8" явно лишнее, реально память начитается по первому адресу, откуда оно берется и почему оно на разных машинах разное?

    Спасибо.
     
  2. Pavia

    Pavia Well-Known Member

    Публикаций:
    0
    Регистрация:
    17 июн 2003
    Сообщения:
    2.409
    Адрес:
    Fryazino
    Обычно BAR адресс вещь не фиксированная. Установкой BAR регистров занимается BIOS.

    Bass address registers - BAR
    Bass address (базовый адрес)-это адрес регистров устройства. Обычно эти регистры отображаются на физические адреса памяти. Или более устаревшая модель в адресатное пространство портов ввода/вывода. Это и есть основные регистры PCI устройства. Через них происходит программирование, но информация эта зависит от производителя.

    Более детально о Bass address.

    Если все биты 32 установлены в 0, то Bass address не записан в ячейку.
    Если бит 0 установлен в 1, то Bass address задает базовый адрес в пространстве портов I/O.
    Иначе если бит 0 установлен в 0, то Bass address задает базовый адрес в пространстве адресов памяти.
    Если наш Bass address задает базовый адрес в пространстве портов I/O. То он имеет следующий формат.
    Код (Text):
    1. +------------------------------------------------+-+-+
    2. |31                                             2|1|0|
    3. +------------------------------------------------+-+-+
    4. |         Bass address                           |0|1|
    5. +------------------------------------------------+-+-+
    32 бита.
    Нулевой бит указывает на то, что это пространство портов I/O.
    Первый бит зарезервирован.
    Остальные указываюсь адрес базового порта ввода вывода.

    Если наш Bass address задает базовый адрес в пространстве памяти. То он имеет следующий формат.
    Код (Text):
    1. +--------------------------------------------+-+-+-+-+
    2. |                                           4|3|2|1|0|
    3. +--------------------------------------------+-+-+-+-+
    4. |         Bass address                       | |   |0|
    5. +--------------------------------------------+-+-+-+-+
    Либо 32 бита, либо 64 бита.
    Нулевой бит указывает на то, что это пространство памяти.
    Следующие два бита 1 и 2 определяют тип записи.
    00- 32 битная запись любой адрес
    01- 32 битная запись, но адрес меньше мегабайта.
    02- 64 битная запись любой адрес
    03- зарезервировано
    Бит 3 – Prefetchable – предпочтения выставляется в один если не вызывает побочных явления при чтении. Если у нас отображаются регистры в память. Так называемые Memory Mapped I/O то при чтении могут срабатывать триггеры, которые могут влиять на работу устройства. Поэтому для регистров этот бит всегда опущен (not prefetchable). А вот для линейного буфера видео карты этот бит выставлен (Prefetchable).

    Остальные биты базовый адрес.
    Если вы хотите узнать размер памяти, которая отображается в адресное пространство. Или число портов ввода вывода. То вы должны выставить максимальное значение FFFFFFFF и прочитав его. Устройство обнулит биты, которые соответствуют его диапазону адресов. Замечу, что выставляемый адрес должен быть кратным размеру его диапазона, выровнен по этому размеру, поэтому и происходит обнуление битов.


    Всякое PCI устройство может иметь свой встроенный BIOS.
    Которая располагается по Expansion ROM Base Address. Стандартный диапазон для всех БИОСов PCI устройств у PC-AT совместных компьютеров 0C0000h-0DFFFFh.
    Expansion ROM Base Address – Имеет следующий формат
    Код (Text):
    1. +--------------------------------+-----------------+-+
    2. |31                            11|10              1|0|
    3. +--------------------------------+-----------------+-+
    4. |Expansion ROMBase Address       | резерв          |1|
    5. +--------------------------------+-----------------+-+
    Нулевой бит Expansion ROM Enable это бит, который отвечает - будет ли задействован БИОС или нет.
    Expansion ROMBase Address – старшие 21 бит адреса БИОСа. Если вы попробуете записать значение 0FFFFFFFEh тогда мы получим в ответ размер БИОСа. После нужно восстановить адрес, предварительно схоронив его.
    Если вернулись все 0, то БИОСа не существует. Если смотреть от младших битов к старшим, то там идут нули первый единичный бит сигнализирует размер БИОСа. Пример
    |11111111 11111111 00000|000 0000000|X|
    Размер 64Кб его проще всего вычислить по формуле
    Size:=-Addr; Или команда ассемблера NEG
    Размер БИОСа может колебаться в приделах от 64КБ до 16МБ. А его адрес должен быть кратен размеру БИОСа.
    Для видео карты БИОС должен располагаться по адресу 0C0000h поэтому в
    Expansion ROMBase Address у нее может быть 0.

    Это все и многое другое описанно в спецификации PCI. Даже есть руский перевод на версию 2.0 PCI_Rev_20_rus
     
  3. misha_irpen

    misha_irpen New Member

    Публикаций:
    0
    Регистрация:
    19 мар 2008
    Сообщения:
    6
    Pavia
    А если устройство имеет несколько диапазонов отображаемой памяти, то можно как-то узнать какой кусок куда попал или только по предполагаемому размеру можно предположить? Или не бывает таких устройств, чтобы отображали несколько участков с одинаковым набором флагов в младших битах?

    Спасибо за наводку, в общих чертах понял, буду пробовать.
     
  4. Pavia

    Pavia Well-Known Member

    Публикаций:
    0
    Регистрация:
    17 июн 2003
    Сообщения:
    2.409
    Адрес:
    Fryazino
    В спецификации железки точно прописанно, какой BAR за что отвечает. А адресс узнается путем чтения его содержимого, у каждого BAR он свой.
    А что за железка узнаем по Vendor_ID, Device_ID Sub_Device_ID и тд...

    Бывают. Отчего же не быть?
     
  5. misha_irpen

    misha_irpen New Member

    Публикаций:
    0
    Регистрация:
    19 мар 2008
    Сообщения:
    6
    Pavia
    То есть не зависимо от системы у конкретной железки каждый BAR всегда будет иметь один и тот же смысл? Документации на железку нет, приходится методом тыка все определять.
     
  6. Pavia

    Pavia Well-Known Member

    Публикаций:
    0
    Регистрация:
    17 июн 2003
    Сообщения:
    2.409
    Адрес:
    Fryazino
    misha_irpen
    Да, кто же себе будет жизнь усложнять.
     
  7. misha_irpen

    misha_irpen New Member

    Публикаций:
    0
    Регистрация:
    19 мар 2008
    Сообщения:
    6
    Pavia
    Это понятно, спасибо. Но обнаружилась новая проблема: железка возвращает сильно завышенный объем памяти. Если реально на ней распаяно два мегабайта, то почему после записи FFFFFFFF она возвращает FFC00000? Это уже 4 мегабайта. Причем при записи выше законных двух метров начитаются сильные задержки, а чтение из этих адресов всегда возвращает одно и то же значение (в зависимости от системы это либо 00 либо FF), т.е. никаких четырех мегабайт там точно нет и не было.

    На железку пинать не могу, потому что на проверку этим грешат например и видеокарты. Вот собрал по имеющимся статистику (первая пара чисел -- реальный диапазон видеопамяти, третье число -- результат чтения и инверсии соответствующего BAR после записи туда FFFFFFFF):
    D0000000..D7FFFFFF, DFFFFFFF (GF7300GS/128M)
    E0000000..E0FFFFFF, E1FFFFFF (VANTA/16M)
    E6000000..E6FFFFFF, E7FFFFFF (TNT/16M)
    E6000000..E67FFFFF, E6FFFFFF (TNT/8M)
    E6000000..E63FFFFF, E67FFFFF (SIS/4M)
    F0000000..F0FFFFFF, F7FFFFFF (VMWare/16M)

    Причем если присмотреться, то можно увидеть что во всех кроме последнего (весьма специфического) случаях объем завышен ровно в два раза, т.е. выставлен один лишний бит.

    Как же узнать реальный размер памяти?
     
  8. Pavia

    Pavia Well-Known Member

    Публикаций:
    0
    Регистрация:
    17 июн 2003
    Сообщения:
    2.409
    Адрес:
    Fryazino
    misha_irpen
    Скорее всего ошибка в коде. У меня таких проблем не было. Темболее BAR регистров от 1 до 6 и в видео картах один используется под память другой под регистры отоброженные в память.
     
  9. misha_irpen

    misha_irpen New Member

    Публикаций:
    0
    Регистрация:
    19 мар 2008
    Сообщения:
    6
    Pavia
    Вот мой код:
    Код (Text):
    1. function ReadConfigDWord(const Bus, Device, Func: Byte; const Reg: Word): DWord;
    2. var
    3.   Regs: TRealRegs;
    4. begin
    5.   Regs.AX:= $B10A;
    6.   Regs.BL:= (Func and $07) or (Device shl 3);
    7.   Regs.BH:= Bus;
    8.   Regs.DI:= Reg;
    9.   RealIntr($1A, Regs);
    10.   if Regs.AH = $00 then
    11.     Result:= DWord(Regs.ECX)
    12.   else
    13.     Result:= $FFFFFFFF;
    14. end;
    15.  
    16. function WriteConfigDWord(const Bus, Device, Func: Byte; const Reg: Word; const Data: DWord): Boolean;
    17. var
    18.   Regs: TRealRegs;
    19. begin
    20.   Regs.AX:= $B10D;
    21.   Regs.BL:= (Func and $07) or (Device shl 3);
    22.   Regs.BH:= Bus;
    23.   Regs.DI:= Reg;
    24.   Regs.ECX:= Integer(Data);
    25.   RealIntr($1A, Regs);
    26.   Result:= (Regs.AH = $00);
    27. end;
    28.  
    29. function GetAddressRange(const Bus, Device, Func: Byte; const Reg: Word; out Address, Range: DWord): Boolean;
    30. var
    31.   Org: DWord;
    32. begin
    33.   Org:= ReadConfigDWord(Bus, Device, Func, Reg);
    34.   if (Org <> 0) and (Org <> $FFFFFFFF) and (Org and $00000001 = 0) and WriteConfigDWord(Bus, Device, Func, Reg, $FFFFFFFF) then
    35.     begin
    36.       Address:= Org and $FFFFFFF0;
    37.       Range:= not (ReadConfigDWord(Bus, Device, Func, Reg) and $FFFFFFF0);
    38.       WriteConfigDWord(Bus, Device, Func, Reg, Org);
    39.       Result:= True;
    40.     end
    41.   else
    42.     Result:= False;
    43. end;
    Я обшариваю все шины и когда нахожу VendorID <> FFFF, натравливаю на найденное устройство GetAddressRange (при этом Func = 0; Reg = [10h, 14h, 18h, 1Ch, 20h, 24h]), а потом вывожу на экран полученные Address и Range.
     
  10. Pavia

    Pavia Well-Known Member

    Публикаций:
    0
    Регистрация:
    17 июн 2003
    Сообщения:
    2.409
    Адрес:
    Fryazino
    misha_irpen
    Да все верно. Значит раньше этого не замечал. У видюшак есть еще одна особенность это GART возможно в этом дело.

    Тогда надо смотреть может где еще зашит реальный размер. Так, как у тебя самодельная железка то новерно можно попробовать привезаться к Subsystem_ID, Revision_ID. Надо будет составить базу.
     
  11. misha_irpen

    misha_irpen New Member

    Публикаций:
    0
    Регистрация:
    19 мар 2008
    Сообщения:
    6
    Ладно, спаисбо за советы. Буду думать.