Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: NIOSII - Странный глюк
Форум разработчиков электроники ELECTRONIX.ru > Программируемая логика ПЛИС (FPGA,CPLD, PLD) > Работаем с ПЛИС, области применения, выбор
RHnd
Здравствуйте! Наконец-то, после долгого чтения мануалов я написал свой модуль для SOPC. Написал небольшую программку для NIOSII с целью протестировать модуль и столкнулся с очень странным для меня глюком.

Сначала о модуле:
Два порта, мастер и слейв. У слейва три регистра: CNTRL, STATUS и ADR. CNTRL: 0-ой бит – go. NIOSII выставляет этот бит, модуль начинает работать, на следующий такт бит автоматически сбрасываетя. STATUS: 2-ой бит – error, этот бит выставляется, если произошла ошибка. 1-ый бит – busy, выставлен пока модуль работает. 0-ой бит – done, выставлется когда работа модуля закончена. ADR – в этот регистр NIOSII записывает адрес. Работает модуль (по моей задумке, по крайней мере) так: выставляется бит go, логика модуля по этому биту считывает данные извне (свитчи на плате) и передает их мастер-порту. Мастерпорт, получив эти данные, записывает их по адресу из регистра ADR слейва, потом читает данные оттуда же. Если данные совпадают, то работа модуля заканчивается, если не совпадают, то выставляется бит error.

Код программы для NIOSII:
Код
#include <stdlib.h>
#include "alt_types.h"
#include <stdio.h>
#include <unistd.h>
#include "system.h"
#include "sys/alt_irq.h"
#include "altera_avalon_pio_regs.h"
#include "TestM_regs.h"

int ButFlag=0;

static void handle_button_interrupts(void* context, alt_u32 id)
{
  *((alt_u8*)context)=1;
  IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUT_BASE, 0);
}

static void init_button_pio()
{
  IOWR_ALTERA_AVALON_PIO_IRQ_MASK(BUT_BASE, 0x1);
  IOWR_ALTERA_AVALON_PIO_EDGE_CAP(BUT_BASE, 0x0);
  alt_irq_register( BUT_IRQ, (void*) &ButFlag, handle_button_interrupts );
}

int main()
{
  alt_u32 led=0xAAAA;
  init_button_pio();

  while(1)
  {
    if (ButFlag){
      IOWR_TESTM_ADR(TESTM_0_BASE,(int)&led);
      IOWR_TESTM_CTRL(TESTM_0_BASE,0x1);      //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      while (IORD_TESTM_STATUS(TESTM_0_BASE) & 0x1);
      IOWR_ALTERA_AVALON_PIO_DATA(LEDR_BASE,led);
      ButFlag=0;
    }
  }
  return 0;
}


Суть глюка: если прогу запустить как Run As, то LEDR (лампочки на плате) выводят 0xAAAA, а не то, что выставлено свичами. При этом бит ошибки не выставляется. Если запустить как Debug as NIOSII Hardware, то начинаются странности. Если на любой строчке до помеченной восклицательными знаками включительно сделать step (F5 или F6), то все прекрасно работает, выводится именно то, что на свичах. Если же с самого начала дебагинга сделать resume (F8) или же поставить брейкпоинт за помеченной строчкой, то прога ведет себя как при Run As. В чем суть глюка я понять не могу решительно. Адрес в ADR пишется, бит done выставляется, error – нет. Грешил на SDRAM, но проект делал на основе альтеровского примера ниоса для этой самой платы (Starter kit - cyclon 2). Попробовал снизить частоту со 100 до 50 – не помогло. На всякий случай прикладываю файлы головного модуля, модуля pll и reset.
Подскажите, в чем может быть дело и где вообще искать решение?

PS: Я еще только начинаю. Если кому-то вдруг будет не лень просмотреть мои верилоговские файлы и указать всякие ошибки перехода от C к Verilog? поправить стиль и т.п., то я буду очень благодарен!!!

Прикладываю файлы:
TESTM.v, TESTM_L.v, TESTM_M.v, TESTM_S.v - верилоговские файлы, описывающие сам модль.
TestM_regs.h -файл с макросами для работы с модулем.
sdram_pll.v и reset_delay.v - соответственно названию.
NT.v - головной модуль проекта.

Кстати, пытался добавить квартовский архив проекта, но "не имеете прав на добавление файла с разрешением" qar. Почему?
id_gene
Ну во-первых, вы не приложили модуль syst.
Во-вторых, лучше бы вы все файлы в одном архиве выложили рядом, чтобы разом скачать, для желающих.

Самое главное - вы систему моделировали в моделсиме до синтеза?
Программа небольшая, много времени не займет.

Общие замечания:
много лишнего верилога в регистрах, о чем вам должен был сказать синтезатор.
пример: если вы записываете в регистр, достаточно просто написать
Код
if (write)
    my_reg <= writedata;
и совсем не надо дописывать
Код
else my_reg <= my_reg;
Читать тяжело.

Сейчас я попробую посмотреть, что внутри, если получится - напишу.

А может вы просто в тесте ошибку сделали, и проверяете
while (IORD_TESTM_STATUS(TESTM_0_BASE) & 0x1);
вместо
while (IORD_TESTM_STATUS(TESTM_0_BASE) & 0x2);
Но тогда у вас все висеть должно намертво.
И еще я бы led объявил как volatile, но это вы должны были в дебагере в ассемблере увидеть, что чтениz не происходит.
RHnd
Цитата(id_gene @ Jul 13 2007, 20:20) *
Ну во-первых, вы не приложили модуль syst.
Во-вторых, лучше бы вы все файлы в одном архиве выложили рядом, чтобы разом скачать, для желающих.

Про syst.v не сообразил - прикладываю тут.
Про "рядом" - тоже не сообразил. Сейчас выложу.

Цитата(id_gene @ Jul 13 2007, 20:20) *
Самое главное - вы систему моделировали в моделсиме до синтеза?
Программа небольшая, много времени не займет.

Нет. sad.gif Поскольку моделсимом пока не владею. sad.gif он в очереди на освоение.

Цитата(id_gene @ Jul 13 2007, 20:20) *
Общие замечания:
много лишнего верилога в регистрах, о чем вам должен был сказать синтезатор.

Спасибо. Синтезатор не ругался (варингами, по крайне мере). А он по умолчанию будет считать, что в противном случае значение не меняется? Это гуд, буду знать. Просто у альтеры в примере именно так сделано, а другого образца я не имею - у нас на работе никто верилогом не владеет. smile.gif

Цитата(id_gene @ Jul 13 2007, 21:02) *
А может вы просто в тесте ошибку сделали, и проверяете
while (IORD_TESTM_STATUS(TESTM_0_BASE) & 0x1);

Упс. Просто я в течение дня ковырялся и уходя с работы взял файлы впопыхах. Разумеется, там
Код
while (!(IORD_TESTM_STATUS(TESTM_0_BASE) & 0x1))


Цитата(id_gene @ Jul 13 2007, 21:02) *
И еще я бы led объявил как volatile, но это вы должны были в дебагере в ассемблере увидеть, что чтениz не происходит.

Хочу уточнить, какое именно чтение не происходит? Можете вкратце объяснить, как компилятор среагирует на volatile? Может, действительно тут дело? Хотя, тогда совершение одного шага в дебагере не должно делать код работающим, но делает. Что ввергает меня в шок. sad.gif

Вот файлы:
nt.qar - http://ifolder.ru/2658520 - квартуcoвский архив проекта.
software - http://ifolder.ru/2658543 - архив програмного проекта (вдруг там в настройках syslib дело?).
nt.rar - http://ifolder.ru/2658813 - архив всей папки с проектом, включая и модуль и софт.
id_gene
Цитата(RHnd @ Jul 13 2007, 21:24) *
Можете вкратце объяснить, как компилятор среагирует на volatile?

Например вы читаете в цикле переменную, которая у вас меняется независимо от программы (аппаратным контроллером).
int a = 0;
while (a);
Такую конструкцию компилятор оптимизирует и выкидыает, потому что не увидит в ней смысла, пока вы не объявите переменную как voltile. Тогда компилятор честно вставит чтение.
Как раз ваш случай. Зачем процессору читать второй раз переменную led из памяти, если она уже хранится в стеке или в регистре.


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


Общие замечания по верилогу:
на мой взгляд, слишком много лишних регистров. В каких-то случаях это может быть оправдано, особенно, когда вам нужно все конвееризировать, и сильно не хватает скорости, или когда вы хотите избавиться от лишних переключений в нерабочем состоянии, но вся эта братия занимет ресурсы и межсоединения.
Вот посмотрите, как я переписал ваш модуль. Вы к этому тоже со временем привыкнете, начнете понимать, что и где можно сократить.
Я там все три модуля в один записал, чтобы файлы не плодить.
Синхронизация внешних данных происходит всегда, а не только по стробу старта. Если это критично, то не надо там плодить еще одну машину состояний, просто поставьте два регистра после go_reg, и пользуйтесь ими.
Чтение слейва синхронное, т.е. данные идут из регистра. можно урезать до асинхронного, выиграть такт, но удлиннить путь.
Чтение контрольного регистра я выкинул, потому что полезной информации вы там не найдете.
Машина состояний теперь имеет три состояния. Для увеличения скорости можно добавить еще одно состояние после чтения, чтобы принять прочитанные данные в регистр, и только потом сравнивать с сохраненным значением. Это разгрузит логику данных по чтению, и вы получите большую частоту.

Все вышесказанное мое имхо и, возможно, слегка упрощено. Критика принимается. smile.gif

Ушел.
RHnd
Про volatile примерно понял. Вообще, по симптомавм похоже. Надо будет в понедельник проверить.

Пошел смотреть исправление модуля. Большое спасибо за помощь!!!
RHnd
Цитата(RHnd @ Jul 13 2007, 22:29) *
Пошел смотреть исправление модуля. Большое спасибо за помощь!!!

Посмотрел модуль. Узнал много нового о верилоге, даже несколько вопросов возникло. smile.gif
Помню, читал и слышал, что при использовании if или case нужно описывать все ветви, иначе могут быть траблы. Из вашего кода сделал вывод, что при использование триггеров (когда идет обработка по фронту) это не так, полное описание всех ветвей требуется только при описании логики (always @*). Правильно?

Есть вопрос вот по этому куску кода:

Код
//  asynchronous read version
//      does not hold the value when s_chipselect deasserted
  always @*
    case (s_address)
        2'b01   :   s_readdata = {29'h0, reg_status};
        2'b10   :   s_readdata = reg_address;
        default :   s_readdata = {29'h0, reg_status};  // required in comb logic
        endcase

s_readdata объявлен как reg [31:0]. Мне казалось, что если объявлен как reg, то обрабатываться должен как триггер - по фронту. А здесь формируется логика. Был неправ? И вопрос, который меня давно мучит - почему здесь стоит = а не <=? Пробовал заменить, но в RTL Viewer разницы не увидел. Концепция двух разных присваиваний для меня совсем новая, так до сих пор разницу между ними и не осознал. help.gif

Что касается volatile - вспомнил, что в дебагере добавлял watch &led и смотрел как сам адрес (проверить, что в регистр модуля пишется), так и его содержимое. Оно, вроде, не менялось. Но все равно в понедельник попробую.

А вам большой a14.gif

PS: Странно, что примеры альтеры "не оптимальны". Может, они писались с учетом совместимости с более ранними стандартами verilog? Хотелось бы для развития посмотреть примеры хорошего кода - как надо и как принято. Наилучшим, наверное, было бы посмотреть курс каких-нибудь лабораторных по verilog с примерами качественного исполнения. Если кто-нить может поделиться подобным, то буду очень признателен! r h n d (сбака) m a i l . r u
id_gene
Цитата(RHnd @ Jul 14 2007, 00:10) *
Помню, читал и слышал, что при использовании if или case нужно описывать все ветви, иначе могут быть траблы. Из вашего кода сделал вывод, что при использование триггеров (когда идет обработка по фронту) это не так, полное описание всех ветвей требуется только при описании логики (always @*). Правильно?
Правильно.
Цитата
Есть вопрос вот по этому куску кода: ...
s_readdata объявлен как reg [31:0]. Мне казалось, что если объявлен как reg, то обрабатываться должен как триггер - по фронту. А здесь формируется логика. Был неправ?
Неправ.
Единственное различие между reg и wire в том, что reg можно назначать только в блоке always, а wire только через assing.
Поскольку case в комбинаторике можно использовать только внутри always блока, то здесь как раз объявелн reg.
Есть еще блок initial. В нем вы можете использовать только reg для назначений, но если очень надо wire, то для можно поставить force/release.
Цитата
И вопрос, который меня давно мучит - почему здесь стоит = а не <=?
Поищите в интернете примеры, под рукой нет ни одного. Общее правило таково: в регистрах используются неблокирующие <=, в комбинаторных always блоках - блокирующие =.
Цитата
Что касается volatile - вспомнил, что в дебагере добавлял watch &led и смотрел как сам адрес (проверить, что в регистр модуля пишется), так и его содержимое.
Еще можно в кэш попасть. То есть опять фактического чтения из памяти не произойдет. тут еще можно поразбираться.

ps нормальные примеры у Альтеры. Оптимальность в данном случае - такая тонкая штука.

pps кстати, мой верилог можно еще больше упростить, например
assign m_write = state_machine_reg[0]; и так далее.
RHnd
Цитата(id_gene @ Jul 14 2007, 11:30) *
Есть еще блок initial. В нем вы можете использовать только reg для назначений, но если очень надо wire, то для можно поставить force/release.

Блок initial, вроде, только для моделирования? Он ведь не синтезируемый?
Цитата(id_gene @ Jul 14 2007, 11:30) *
Еще можно в кэш попасть. То есть опять фактического чтения из памяти не произойдет. тут еще можно поразбираться.

Тоже возможно. а как бы мне это проверить? кроме как отключить кеш и собрать заново - идей нет. Поправка - вроде же можно писать мимо кеша выставляя старший бит адресса? Что-то такое видел в документации, но никогда не пользовался.
Цитата(id_gene @ Jul 14 2007, 11:30) *
ps нормальные примеры у Альтеры. Оптимальность в данном случае - такая тонкая штука.

Не, нормальные, рабочие, но они, например, при объявлении модуля перечисляют module A(B,C); input B; output C;
RHnd
Итак, дело оказалось в data cache. Сначала попробовал volatile, но не помогло. После этого отключил нафиг кеш, пересобрал ниос - заработало. Тонкое место найдено. Какие способы решения ситуации я для себя открыл (ну, кроме отключения кеша smile.gif):
1) Вариант, на котором я и остановился:
Код
  alt_u32* p_led=(alt_u32*)alt_uncached_malloc(4);
  *p_led=0xAAAA;
  init_button_pio();

  while(1)
  {
    if (ButFlag){
      IOWR_TESTM_ADR(TESTM_0_BASE,(int)p_led);
      IOWR_TESTM_CTRL(TESTM_0_BASE,0x1);      
      while (!(IORD_TESTM_STATUS(TESTM_0_BASE) & 0x1));
      IOWR_ALTERA_AVALON_PIO_DATA(LEDR_BASE,*p_led);
      ButFlag=0;
    }
  }
  alt_uncached_free((void*)p_led);

2) Второй вариант, вызвавший у меня затруднения.
Код
  volatile alt_u32 led=0xAAAA;
  alt_u32* p_led;
  init_button_pio();

  while(1)
  {
    if (ButFlag){
      IOWR_TESTM_ADR(TESTM_0_BASE,(int)&led);
      IOWR_TESTM_CTRL(TESTM_0_BASE,0x1);      
      while (!(IORD_TESTM_STATUS(TESTM_0_BASE) & 0x1));
      p_led=(alt_u32*)(((int)&led)|0x80000000);
//    p_led=(alt_u32*)alt_remap_uncached((void*)&led,4);
      IOWR_ALTERA_AVALON_PIO_DATA(LEDR_BASE,*p_led);
      ButFlag=0;
    }
  }

Что мне странно: вариант
Код
p_led=(alt_u32*)(((int)&led)|0x80000000);
работает прекрасно, а вот вариант
Код
p_led=(alt_u32*)alt_remap_uncached((void*)&led,4);
не работает. Хотя, по дебагеру значение p_led в обоих случаях одинаковое. В чем тут дело? Может, я не правильно описание функции alt_remap_uncached понял и некорректно ее использую?
vetal
что-то мне не совсем понятно.
1.Для задач общения с периферией существуют специальные команды stwio и ldwio, которым чихать на кэш.
Точнее "ldwio and stwio instructions load and store 32-bit data words from/to peripherals without caching and buffering."
Эти команды доступны через макросы IOWR_32DIRECT и IORD_32DIRECT.
2. Похоже что вы пытаетесь работать с регистрами как с памятью.
3. установка старшего бита адреса как раз и отключает все примочки и преврfщает команду IOWR в IOWR_DIRECT.
RHnd
Цитата(vetal @ Jul 16 2007, 20:17) *
1.Для задач общения с периферией существуют специальные команды stwio и ldwio, которым чихать на кэш.
Эти команды доступны через макросы IOWR_32DIRECT и IORD_32DIRECT.

Т.е. мне надо просто заменить макросы в header? Я свой писал на основе альтеровских, а у них эти макросы (которые direct) не использовались.
Цитата(vetal @ Jul 16 2007, 20:17) *
2. Похоже что вы пытаетесь работать с регистрами как с памятью.

А можно подробнее для тех кто в танке?
Цитата(vetal @ Jul 16 2007, 20:17) *
3. установка старшего бита адреса как раз и отключает все примочки и преврfщает команду IOWR в IOWR_DIRECT.

Для этого я старший бит и устанавливаю.
vetal
Цитата(RHnd @ Jul 16 2007, 20:29) *
Т.е. мне надо просто заменить макросы в header? Я свой писал на основе альтеровских, а у них эти макросы (которые direct) не использовались.

Да, замените iowr на iowr_direct для доступа к регистрам модуля.

Цитата
А можно подробнее для тех кто в танке?

Подробнее чем там не опишу http://www.altera.com/literature/lit-nio2.jsp smile.gif
Более близко - http://www.altera.com/literature/hb/nios2/n2cpu_nii51017.pdf (847к)
Смотреть и описание инструкций stw/stwio и описание макросов в файле io.h
Код
#define IOWR_32DIRECT(BASE, OFFSET, DATA) \
  __builtin_stwio (__IO_CALC_ADDRESS_DYNAMIC ((BASE), (OFFSET)), (DATA))


Цитата
Для этого я старший бит и устанавливаю.

Это не очень удобно при работе с hal ))(см. выше)
RHnd
Спасибо, завтра попробую заменить макросы. Надеюсь, в новые проблемы не вляпаюсь. smile.gif
И все же, чем p_led=(alt_u32*)(((int)&led)|0x80000000) от p_led=(alt_u32*)alt_remap_uncached((void*)&led,4) отличается?
vetal
Практически ничем, она подставляет в старший бит адреса 1.
Код
/*
* Convert a pointer to a block of cached memory, into a block of
* uncached memory.
*/

volatile void* alt_remap_uncached (void* ptr, alt_u32 len)
{
  alt_dcache_flush (ptr, len);
  return (volatile void*) (((alt_u32) ptr) | 0x80000000);
}
RHnd
Цитата(vetal @ Jul 16 2007, 21:15) *
Практически ничем, она подставляет в старший бит адреса 1.

Тогда странно, что получается разный результат.

Сейчас сообразил, что у нас какое-то недопонимание. Итак, модулю дается указатель на переменную - адрес в сдрам. Модуль по этому адресу пишет. Проблема в том, что когда ниос после работы модуля обращается к этой переменной, то получает не записанное модулем значение из сдрам, а значение из кеша. Как мне тут может помочь замена макросов? Макросы ведь относятся к работе модуля, а он и так работатет корректно, мне же надо получить значение переменной из сдрам в обход кеша. Или я что-то не то говорю?
Так и не понял, почему вы считаете, что я пытаюсь работать с регистрами как с памятью?
Заранее спасибо за ответы. Я ведь еще только учусь. smile.gif
vetal
Для чтения используйте макрос IORD_32DIRECT, для чтения непосредственно из памяти а не из кэша.
RHnd
Цитата(vetal @ Jul 16 2007, 21:31) *
Для чтения используйте макрос IORD_32DIRECT, для чтения непосредственно из памяти а не из кэша.

Так, видимо, я совсем туплю. Можно пример кода, как я должен использовать этот макрос внутри ниосовской программы? help.gif
Я пока могу предположить только вот это:
*p_ped=IORD_32DIRECT((int)p_led,0), но мне этот вариант не кажется более предпочтительным чем указанные ранее.
vetal
Расположить там, где это вам нужно.
alt_u32 i;
i= IORD_32DIRECT(BASE_ADDR, OFFSET);
где BASE_ADDR - начало адресного пр-ва чтения,
OFFSET - смещение в адресном пр-ве.

Тоже намудрил чего-то. IOWR и IORD тоже в обход кэша работают, только в качестве второго параметра им нужен номер регистра, где сидит смещение относительно базового адреса.
RHnd
Может таки проще будет uncached_malloc или remap_uncached? Правда, последний у меня, напомню, так и не заработал, я его заменил прямым записыванием 1 в старший разряд.
Кстати, простите, но моего познания в устройстве кеша не хватило, чтоб понять, что делает alt_dcache_flush. sad.gif Т.е., как я понял, оно обновляет данные в памяти данными кеша, а затем этот кусок кеша отключает? Так, что ли? А можно как-нить наоборот - обновить данные кеша данными из памяти?

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

По функции:

"The alt_dcache_flush() function flushes (i.e. writes back dirty data and then invalidates) the data cache for a memory region of length len bytes, starting at address start."
RHnd
Не, в документации-то я посмотрел. Я осмыслить пытаюсь. smile.gif Просто, если оно действительно сначала из кеша память обновляет, то понятно, почему оно у меня не работает. Может, вот это: alt_remap_cached?
vetal
скорее всего так и есть. сейчас пока времени нет проверить. сначала синхронизирует озу с кэшем, а затем дает данным обход кэша.
RHnd
Итак, повозился сегодня с железкой - разобрался с кешем. И с макросами, и с remapom. Большое спасибо vetal и id_gene. a14.gif

Теперь над новой проблемой думаю: увидел в 7.1 встроенное FIFO. Если с ним кто-нить уже работал, то не подскажете, сколько тактов уходит у ниоса на получение из него данных при использовании API?
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.