Jump to content

    
Nagisa

Как правильно и оптимальнее реализовать несколько автоматов

Recommended Posts

делаю на Verloge контроллер SDRAM c обработкой 3х запросов

а именно:

по приоритетам начиная с самого высокого
1. rd  - запрос на чтение из памяти, синхронный
2. rdb - запрос на пакетное чтение, отложенный - те можем немного ждать
3. wr  - запрос на запись, полностью асинхронный, делаем когда можем

Соответственно RD - синхронный, ждем результата и отдаем внешней системе

WR это адрес+данные в регистр и отработаем когда будем свободны, но запомнить факт поступления запроса мы должны

тк запрос на запись может поступить во время пакетного чтения.

RB  - запрос пакетного чтения 128 слов, можем немного подождать и завершить процессы чтения/записи

общий автомат памяти
 

localparam STATE_FIRST             = 4'd0;       // first state in cycle
localparam STATE_CMD_START         = 4'd1;       // state in which a new command can be started
localparam STATE_CMD_CONT          = 4'd4;        // STATE_CMD_START  + RASCAS_DELAY; // 4 command can be continued
localparam STATE_CMD_READ_STOP     = 4'd5;        // остановка чтения, примеряется при однословном чтении
localparam STATE_CMD_READ          = 4'd7;       // данные готовы
localparam STATE_LAST              = 4'd15;      // last state in cycle

reg [3:0] q;


always @(posedge clk)
begin
    if (reset_done==1 && ct_rb_zr==1)                         // процедура инициализации завершена и нет чтения пакета
            q <= q + 3'd1;                                         // тогда меняем состояение автомата
end

 

кусок кода с автоматами обработки запросов

// автомат состояний очереди записи
localparam STATE_W_WAIT        =2'd1;
localparam STATE_W_PREPARE    =2'd2;
localparam STATE_W_WRITE    =2'd3;
localparam STATE_W_WRITED    =2'd0;

reg [1:0] statew;

always @ (posedge clk)
begin
   if(((statew==STATE_W_WAIT   ) && (wr==1 )) ||                                 // мы готовы к приему сигнала записи и он поступил - идем в STATEW_PREPARE
        ((statew==STATE_W_PREPARE) && (q == STATE_FIRST) && oe==0 && rb==0) ||  // есть данные на запись + есть готовность к началу обработки + ждем завершения других процессов
      ((statew==STATE_W_WRITE  ) && (q == STATE_LAST))  ||                      // запись завершена
        ((statew==STATE_W_WRITED ) && (wr== 0))                                 // исходная система завершила цикл записи
        )        
            statew <= statew + 2'd1;     // меняем статус вперед
end
wire we= statew[1] ;

// автомат состояний очереди чтения
localparam STATE_R_WAIT        =2'd1;
localparam STATE_R_PREPARE    =2'd2;
localparam STATE_R_READ        =2'd3;
localparam STATE_R_READED    =2'd0;

reg [1:0] stater;

always @ (posedge clk)
begin
   if(((stater==STATE_R_WAIT   ) && (rd== 1 && we==0 && rb==0)) || // мы готовы к приему сигнала чтения и он поступил + есть готовность к началу обработки - идем в STATER_PREPARE
        ((stater==STATE_R_PREPARE) && (q == STATE_FIRST)) ||       // есть команда на чтение 
      ((stater==STATE_R_READ   ) && (q == STATE_LAST))  ||         // чтение завершено
        ((stater==STATE_R_READED ) && (rd== 0))                    // исходная система завершила цикл чтения
        )        
            stater <= stater + 2'd1;     // меняем статус вперед
end

wire oe=stater[1];


// автомат состояний чтения строки в буфер
localparam STATE_RB_WAIT        =2'd1;
localparam STATE_RB_PREPARE    =2'd2;
localparam STATE_RB_READ        =2'd3;
localparam STATE_RB_READED       =2'd0;

reg [1:0] staterb;

always @ (posedge clk)
begin
   if(((staterb==STATE_RB_WAIT   ) && (rdb== 1)) ||                              // мы готовы к приему сигнала пакетного чтения и он поступил - идем в STATERB_PREPARE
        ((staterb==STATE_RB_PREPARE) && (q == STATE_FIRST) && we==0 && oe==0) || // есть команда на чтение + есть готовность к началу обработки
      ((staterb==STATE_RB_READ   ) && (q == STATE_LAST))  ||                     // запись завершена
        ((staterb==STATE_RB_READED ) )                                           // исходная система завершила цикл чтения
        )        
            staterb <= staterb + 2'd1;     // меняем статус вперед
end

wire  rb= staterb[1];

reg  [6:0]    ct_rb;  // счетчик burst-а

wire startrb=(q==STATE_CMD_READ) && (staterb==STATE_RB_READ);
wire ct_rb_zr=~(|ct_rb); 

always @ (posedge clk)
begin
    if ( (ct_rb_zr==1 &&  startrb==1) || (ct_rb_zr==0) ) // условия счета
        ct_rb<=ct_rb+1;
end

 

в куске пакетного чтения как только у нас наступает статус  STATE_CMD_READ  - я торможу автомат и читаю 128 слов

при одиночных чтениях/записях конфликта работает, при интенсивном использовании - глюки

нутром чую что делаю не так, но не могу сообразить как это правильнее - подскажите

 

также вопрос - что лучше для определения ноля ?
 

reg  [6:0]    ct_rb;  // счетчик burst-а

wire ct_rb_zr=(ct_rb==0);

или

wire ct_rb_zr=~(|ct_rb);  

в первом случае в RTL я вижу модуль сравнения, во втором простой логический элемент

к чему лучше стремиться ?

конструкция  с большим if лучше чем case с if-ами в каждом состоянии ?

 

имеет ли смысл делать вместо счетчика состояний автомата делать сдвиговый регистр, и уже проверять состояния автомата по нужному биту в регистре ? те не надо сравнивать регистр целиком а достаточно 1 бита ?

прошу направить на путь правильный

 

 

 

Share this post


Link to post
Share on other sites
8 minutes ago, Flip-fl0p said:

Прежде чем заниматься оптимизацией - назовите критерий оптимизации.

скорость

те у меня циклон 2 и я хочу 130MHz на SDRAM. без бурста на 130 работает, с бурстом нет.

конечно, в крайнем случае я могу опустится до 65MHz ....

Share this post


Link to post
Share on other sites
17 минут назад, Nagisa сказал:

скорость

те у меня циклон 2 и я хочу 130MHz на SDRAM. без бурста на 130 работает, с бурстом нет.

конечно, в крайнем случае я могу опустится до 65MHz ....

Я бы для начала реализовал автомат в его классическом стиле. 

Более того, такие конструкции раскладываются в большую и сложную логику:

always @ (posedge clk)
begin
   if(((stater==STATE_R_WAIT   ) && (rd== 1 && we==0 && rb==0)) || // мы готовы к приему сигнала чтения и он поступил + есть готовность к началу обработки - идем в STATER_PREPARE
        ((stater==STATE_R_PREPARE) && (q == STATE_FIRST)) ||       // есть команда на чтение 
      ((stater==STATE_R_READ   ) && (q == STATE_LAST))  ||         // чтение завершено
        ((stater==STATE_R_READED ) && (rd== 0))                    // исходная система завершила цикл чтения
        )        
            stater <= stater + 2'd1;     // меняем статус вперед
end

Я бы порекомендовал поработать над архитектурой контроллера, а потом уже над кодом.

Для запросов - сделайте 3 разных буфера: rd, rdb , wr. И организовывайте арбитраж между этими буферами.

Я когда делал SDRAM контроллер, пришел к выводу, что контроллер должен иметь максимально простой и удобный интерфейс. 

Адрес откуда читать\писать.

Количество слов сколько писать\читать.

Тип запроса, адрес или чтение.

Можно чуть усложнить, добавив ID запроса и его обработку. Но это уже мелочи.

Контроллер представлял собой автомат, выполняющий циклограмму SDRAM. Модуль формирования задержек, на основании которых переключался автомат. И входной буфер для хранения нескольких запросов.

Над таким интерфейсом очень удобно ставить внешние арбитры, которые уже будут рулить приоритетами. Разбивать запросы на более мелкие и пр ништяки. Т.е задачей контроллера SDRAM - отработать задание. А что ему указали делать, пусть рулят арбитры)))

 

Share this post


Link to post
Share on other sites
15 minutes ago, Flip-fl0p said:

Я бы для начала реализовал автомат в его классическом стиле. 

Более того, такие конструкции раскладываются в большую и сложную логику:


always @ (posedge clk)
begin
   if(((stater==STATE_R_WAIT   ) && (rd== 1 && we==0 && rb==0)) || // мы готовы к приему сигнала чтения и он поступил + есть готовность к началу обработки - идем в STATER_PREPARE
        ((stater==STATE_R_PREPARE) && (q == STATE_FIRST)) ||       // есть команда на чтение 
      ((stater==STATE_R_READ   ) && (q == STATE_LAST))  ||         // чтение завершено
        ((stater==STATE_R_READED ) && (rd== 0))                    // исходная система завершила цикл чтения
        )        
            stater <= stater + 2'd1;     // меняем статус вперед
end

Я бы порекомендовал поработать над архитектурой контроллера, а потом уже над кодом.

Для запросов - сделайте 3 разных буфера: rd, rdb , wr. И организовывайте арбитраж между этими буферами.

Я когда делал SDRAM контроллер, пришел к выводу, что контроллер должен иметь максимально простой и удобный интерфейс. 

Адрес откуда читать\писать.

Количество слов сколько писать\читать.

Тип запроса, адрес или чтение.

Можно чуть усложнить, добавив ID запроса и его обработку. Но это уже мелочи.

Контроллер представлял собой автомат, выполняющий циклограмму SDRAM. Модуль формирования задержек, на основании которых переключался автомат. И входной буфер для хранения нескольких запросов.

Над таким интерфейсом очень удобно ставить внешние арбитры, которые уже будут рулить приоритетами. Разбивать запросы на более мелкие и пр ништяки. Т.е задачей контроллера SDRAM - отработать задание. А что ему указали делать, пусть рулят арбитры)))

 

а можно конкретнее ? я потому и спрашиваю как это правильно сделать тк сам затупил.

изначально я взял автомат синхронизированный с шиной, переделал на асинхронный вариант (те автомат состояний крутится постоянно), сделал защелку, прикрутил полную корректную инциализационную процедуру, все на 130 завелось

а вот стал прикручивать burst и тут столкнулся с проблемами.

собственно бурст мне нужен для упреждающего чтения строки в внутриплисовую память, из которой она размеренно пойдет на VGA

 

если есть примеры реализации контроллера памяти для обслуживания проца и видеопамяти будет очень полезно

 

идею с упрощением я понял (и пытался максимально унифицировать, включив бурст на чтение постоянно, но в обычном режиме посылая прерывание чтения), но не понял как это сделать если у меня разные адресаты и мне точно надо разделять чтение строки и одного слова. как тут правильнее сделать автомат ?  и таки хочется получить ответы на вопросы в конце

 

 

Share this post


Link to post
Share on other sites
2 часа назад, Nagisa сказал:

также вопрос - что лучше для определения ноля ?

 


reg  [6:0]    ct_rb;  // счетчик burst-а

wire ct_rb_zr=(ct_rb==0);

или


wire ct_rb_zr=~(|ct_rb);  

в первом случае в RTL я вижу модуль сравнения, во втором простой логический элемент

 

Без разницы. В итоге должно получиться одно и тоже. RTL -- это как написано, так и показывает. Дальше в дело вступает оптимизатор, и смотреть надо после синтеза и разводки.

Соревноваться в оптимизирующим компилятором бессмыссленно.

Share this post


Link to post
Share on other sites
2 часа назад, Nagisa сказал:

а можно конкретнее ? я потому и спрашиваю как это правильно сделать тк сам затупил.

изначально я взял автомат синхронизированный с шиной, переделал на асинхронный вариант (те автомат состояний крутится постоянно), сделал защелку, прикрутил полную корректную инциализационную процедуру, все на 130 завелось

а вот стал прикручивать burst и тут столкнулся с проблемами.

собственно бурст мне нужен для упреждающего чтения строки в внутриплисовую память, из которой она размеренно пойдет на VGA

 

если есть примеры реализации контроллера памяти для обслуживания проца и видеопамяти будет очень полезно

 

идею с упрощением я понял (и пытался максимально унифицировать, включив бурст на чтение постоянно, но в обычном режиме посылая прерывание чтения), но не понял как это сделать если у меня разные адресаты и мне точно надо разделять чтение строки и одного слова. как тут правильнее сделать автомат ?  и таки хочется получить ответы на вопросы в конце

 

 

На самом деле в SDRAM нет приниципиальной разницы читать BURST или нет. Скорость работы это не изменит. Burst просто позволяет за 1 запрос к памяти получить больше данных. Не более. В VGA можно спокойно успеть считать из SDRAM памяти данные во время интервалов гашения.

Хотите правильно - рисуйте схему. Будет схема - можно будет что-то обсуждать более конкретно. 

 

Share this post


Link to post
Share on other sites
On 2/19/2021 at 1:36 AM, Flip-fl0p said:

Хотите правильно - рисуйте схему. Будет схема - можно будет что-то обсуждать более конкретно.

@Nagisa помимо схемы, смените стиль описания ваших автоматов на case(state) bla-bla-bla. Это поможет вам увидеть ваши логические ошибки. Ну и нормальный tb с рандомной бомбардировкой контороллера вам тут сильно поможет в отладке.

PS. Ну и SDRAM работает же в режиме постоянного чтения ряда, пока не потребуется переход на другой ряд. Поэтому с точки зрения доступа, что burst, что одиночный доступ, схожи (в схеме с ручным precharge). Может быть в эту сторону порыть? Тогда у вас будет автомат записи и автомат чтения. Но вообще @Flip-fl0p дело советует, один автомат, а все остальное на внешнем арбитре, который управляет очередями и формирует поток. 

ЗЫ. Если поможет, вот в свое время баловался https://opencores.org/projects/hssdrc

Share this post


Link to post
Share on other sites

  Необходима НИР по исследованию временных диаграмм каждой исполняемой команды и группы команд. Эти команды выделяются отдельно и запускаются в цикле по одной, двум и т.д. с целью проверки на возможность параллельной работы. 

Share this post


Link to post
Share on other sites
On 2/19/2021 at 11:20 AM, des00 said:

@Nagisa помимо схемы, смените стиль описания ваших автоматов на case(state) bla-bla-bla. Это поможет вам увидеть ваши логические ошибки. Ну и нормальный tb с рандомной бомбардировкой контороллера вам тут сильно поможет в отладке.

PS. Ну и SDRAM работает же в режиме постоянного чтения ряда, пока не потребуется переход на другой ряд. Поэтому с точки зрения доступа, что burst, что одиночный доступ, схожи (в схеме с ручным precharge). Может быть в эту сторону порыть? Тогда у вас будет автомат записи и автомат чтения. Но вообще @Flip-fl0p дело советует, один автомат, а все остальное на внешнем арбитре, который управляет очередями и формирует поток. 

ЗЫ. Если поможет, вот в свое время баловался https://opencores.org/projects/hssdrc

на счет стиля, с точки зрения красоты, да case-вид лучше, но вот тестирование показало что:

 

wire [3:0] run_cmd =
	(act					 	&&	(q == STATE_CMD_START		))?CMD_ACTIVE: 			// есть чтение или запись -> начинаем процесс
	(!act						&&	(q == STATE_CMD_START		))?CMD_AUTO_REFRESH: 	// нет ничего - авторефреш
	( wre					 	&&	(q == STATE_CMD_CONT 		))?CMD_WRITE:				// we -> запись
	(( oe || rb )			&&	(q == STATE_CMD_CONT 		))?CMD_READ:				// не we + oe -> чтение
	( oe            	   &&	(q == STATE_CMD_READ_STOP  ))?CMD_BURST_TERMINATE: // тормозим чтение
	CMD_INHIBIT;

работает на 130MHz

а вот это нет- максимум  108

wire [3:0] run_cmd;
always @ *
begin
    case (q)
        STATE_CMD_START:      
            if (act)
                run_cmd=CMD_ACTIVE;  
            else
                run_cmd=CMD_AUTO_REFRESH;
        STATE_CMD_CONT:        
            if (wre)
                run_cmd=CMD_WRITE;    
            else if ( oe || rb )
                run_cmd=CMD_READ;
        STATE_CMD_READ_STOP:    
            if (oe)  
                run_cmd=CMD_BURST_TERMINATE;
        default:
                run_cmd=CMD_INHIBIT;
    endcase
end

 

 

Share this post


Link to post
Share on other sites

Приветствую!

54 minutes ago, Nagisa said:

работает на 130MHz

а вот это нет- максимум  108

Это  не эквивалентные описания :unknw: соответственно и результат после  синтеза  будет другим.

Например  чему будет равен   run_cmd    в состоянии STATE_CMD_CONT  если wre==0  и ( oe || rb )==0  :scratch_one-s_head:

Надо добавить еще одну строчку  чтобы  сделать оба варианта  эквивалентными.   

 

Удачи! Rob.

Share this post


Link to post
Share on other sites
3 hours ago, RobFPGA said:

Приветствую!

Это  не эквивалентные описания :unknw: соответственно и результат после  синтеза  будет другим.

Например  чему будет равен   run_cmd    в состоянии STATE_CMD_CONT  если wre==0  и ( oe || rb )==0  :scratch_one-s_head:

Надо добавить еще одну строчку  чтобы  сделать оба варианта  эквивалентными.   

 

Удачи! Rob.

справедливо, но увы 130 не тянет.

 

Share this post


Link to post
Share on other sites
37 минут назад, Nagisa сказал:

справедливо, но увы 130 не тянет.

 

Тут есть что улучать. Без полного кода тяжко что-либо посоветовать, но мне кажется, что можно конвееризировать Ваш код. Да и атрибуты синтеза могут помочь. Например сделать автомат в виде ONE_HOT

Share this post


Link to post
Share on other sites

да, я сейчас планирую кардинально переделать автомат, те сделать его приоритетом burst-чтение

ибо сейчас или хвост или нос - или нормально работает burst-чтение

или операции с шиной, совмещение несет проблемы.

 

 

Share this post


Link to post
Share on other sites
32 минуты назад, Nagisa сказал:

да, я сейчас планирую кардинально переделать автомат, те сделать его приоритетом burst-чтение

ибо сейчас или хвост или нос - или нормально работает burst-чтение

или операции с шиной, совмещение несет проблемы.

 

 

Подумайте вот над чем. Нужен ли Вам Burst. Ибо никакого преимущества скорости он не дает. 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.