|
Как в одном клок-домейне сказать Квартусу, что де инструкция может выполняться несколько тактов? |
|
|
|
Aug 2 2013, 19:23
|
вопрошающий
    
Группа: Свой
Сообщений: 1 726
Регистрация: 24-01-11
Пользователь №: 62 436

|
Всем привет, есть что-то типа примитивного софт-процессора - CmdAddr - номер выполняемого оператора, Cmd - код оператора, и с десяток различных инструкций. Если оставить все инструкции и собрать проект, то клок можно выставить только в 50МГц (есть долгие инструкции, в качестве примера, я написал инструкцию деления). Если закомментировать несколько "долгих инструкций", то проект можно собрать с клоком в 200МГц. Реально я понимаю, что, если я для самого себя условлюсь, что после некоторых инструкций я поставлю 1-2-3 такта пустые инструкции, а длительность выполнения этих инструкций будет не один клок, а 2-3-4, то все будет работать, но вот как это объяснить квартусу? Заводить кучу клоков, синхронизировать данные между ними - получается очень заумно и неудобно. Может есть какой-то простой способ как сказать, что де инструкция Номер 1 и 2 может работать два такта, инструкция Номер 3 может работать 3 такта, инструкия Номер 4 - может работать 4 такта и пусть квартус не парится и не доводит тайминг этих долгих инструкций до высокой частоты? Научите, пожалуйста, меня, кто знает, как это сделать! Спасибо ИИВ Код reg Clk; // клок reg [31:0] In1Reg; reg [31:0] In2Reg; reg [31:0] OutReg; reg [8:0] CmdAddr; reg [17:0] CmdA[0:511]; // список команд-операций как-то где-то инициализируемый reg [17:0] Cmd;
always @(posedge Clk) begin Cmd<=CmdA[CmdAddr+1]; if(Cmd[17]) CmdAddr<=Cmd[8:0]; else begin CmdAddr<=CmdAddr+1; case(Cmd[16:14]) 3'b000: OutReg<=In1Reg+In2Reg; // было бы классно выполнять ее 2 такта 3'b001: OutReg<=In1Reg-In2Reg; // было бы классно выполнять ее 2 такта 3'b010: OutReg<=In1Reg*In2Reg; // было бы классно выполнять ее 3 такта 3'b011: OutReg<=In1Reg/In2Reg; // было бы классно выполнять ее 4 такта 3'b100: OutReg<=In1Reg&In2Reg; // должна выполниться за такт 3'b101: OutReg<=In1Reg^In2Reg; // должна выполниться за такт 3'b110: OutReg<=In1Reg|In2Reg; // должна выполниться за такт 3'b111: // пустой оператор endcase end end
|
|
|
|
|
 |
Ответов
|
Aug 5 2013, 15:11
|
ʕʘ̅͜ʘ̅ʔ
    
Группа: Свой
Сообщений: 1 008
Регистрация: 3-05-05
Пользователь №: 4 691

|
1. Нужно немного переделать модуль: - сделать промежуточные результаты tmp_add, tmp_sub, tmp_mul, tmp_div, etc комбинаторным присвоением - указать оптимизатору, чтоб он не выбрасывал эти промежуточные результаты. Можно использовать директиву // synopsys keep (altera поддерживала, кажется). Можно сделать отдельные модули, которые будут вычислять эти промежуточные результаты, и запретить синтезатору их разгруппировку (я бы сделал так) - в мультиплексоре в регистр записывать эти промежуточные результаты. - в sdc что-то в таком духе (пути только правильно укажите):
set path_stp 3 # например set path_hld [expr ${path_stp} - 1]
set_multicycle_path ${path_stp} -setup -end -through [get_net [list {tmp_xx\[*\]}]] -to [get_cells [list {OutReg_reg\[*\]}]] set_multicycle_path ${path_hld} -hold -end -through [get_net [list {tmp_xx\[*\]}]] -to [get_cells [list {OutReg_reg\[*\]}]]
или -through [get_pins [list ... если делать отдельные модули для результатов
2. формально у Вас In1Reg, In2Reg и управляющий вход мультиплексора меняются каждый такт. Те несколько тактов, которые вы закладываете на вчисление результата, эти цепи не должны меняться. Вам это придется как-то учитывать за пределами области видимости RTL (например, как Вы верно отметили, в микрокоде).
3. CmdA[CmdAddr+1] - это, конечно, "сильно" в плане временных ограничений, учитывая, что CmdAddr вычисляется на предыдущем такте. Сделайте CmdAddr_plus_one, обновляемый вместе с CmdAddr, и используйте его для выборки след. команды.
|
|
|
|
|
Aug 6 2013, 19:46
|
вопрошающий
    
Группа: Свой
Сообщений: 1 726
Регистрация: 24-01-11
Пользователь №: 62 436

|
Огромное спасибо всем отвечавшим и сочувствовавшим, понял примерно как сделать, также сразу, на основе Ваших советов, придумал как улучшить. Сейчас немного уперся в то, что не сильно понимаю, почему у меня не работает команда с мультициклом. Так как мне нужна фактически стейт-машина с микрокодом, а не процессор, поле деятельности как это все модифицировать большое, поэтому я все переписал так, что есть некоторые регистры (я их обозвал GPIO0, GPIO1, ..., GPIO9), которые я как-то инициализирую на каждый такт, и есть несколько результатов операций, которые каждый или почти каждый такт выполняются. Код module main(...)
my_cpu my_cpu_module(...);
endmodule
module my_cpu(...) ... reg signed [REG_LEN-1:0] GPIO0; reg signed [REG_LEN-1:0] GPIO1; reg signed [REG_LEN-1:0] GPIO2; reg signed [REG_LEN-1:0] GPIO3; reg signed [REG_LEN-1:0] GPIO4; reg signed [REG_LEN-1:0] GPIO5; reg signed [REG_LEN-1:0] GPIO6; reg signed [REG_LEN-1:0] GPIO7; reg signed [REG_LEN-1:0] GPIO8; reg signed [REG_LEN-1:0] GPIO9;
// 14 registers reg signed [REG_LEN-1:0] RegRes0; reg signed [REG_LEN-1:0] RegRes1; reg signed [REG_LEN-1:0] RegRes2; reg signed [REG_LEN-1:0] RegRes3; reg signed [REG_LEN-1:0] RegRes4; reg signed [REG_LEN-1:0] RegRes5; reg signed [REG_LEN-1:0] RegRes6; reg signed [REG_LEN-1:0] RegRes7; reg signed [REG_LEN-1:0] RegRes8; reg signed [REG_LEN-1:0] RegRes9; reg signed [REG_LEN-1:0] RegRes10; reg signed [REG_LEN-1:0] RegRes11; reg signed [REG_LEN-1:0] RegRes12;
my_div my_dive_module(.clock(Clk), .denom(GPIO6), .numer(GPIO9), .quotient(RegRes9), .remain(RegRes10)); // должно вычисляться за 8 тактов my_mult my_mult_module(.clock(Clk), .dataa(GPIO5), .datab(GPIO8), .result(RegRes8)); // должно вычисляться за два такта my_sub my_sub_module(.clock(Clk), .dataa(GPIO1), .datab(GPIO7), .result(RegRes7)); // должно вычисляться за один такт my_add my_add_module(.clock(Clk), .dataa(GPIO1), .datab(GPIO7), .result(RegRes6)); // должно вычисляться за один такт my_compare my_compare_module(.clock(Clk), .dataa(GPIO0), .datab(GPIO2), .aeb(CmpFlag[0]), .agb(CmpFlag[1]), .alb(CmpFlag[2])); // должно вычисляться за один такт
always @(posedge Clk) begin RegRes0 <=GPIO0&GPIO2; RegRes1 <=GPIO0|GPIO2; RegRes2 <=GPIO0^GPIO2; RegRes3 <=GPIO3>>1; RegRes4 <=GPIO3<<1; RegRes5 <=~GPIO4; // ... и еще всякие операции по перетаскиванию значений из одного регистра в другой на основе микрокода end endmodule При написании констрейна он не признает его, и я все никак не могу понять почему, констрейн написал вроде бы как все здесь советовали, но, провидимому, так до сих пор что-то до конца не понял. Код set_multicycle_path 8 -setup -end -from [get_registers [list \ {my_cpu_module|GPIO0\[*\] my_cpu_module|GPIO1\[*\] my_cpu_module|GPIO2\[*\] \ my_cpu_module|GPIO3\[*\] my_cpu_module|GPIO4\[*\] my_cpu_module|GPIO5\[*\] \ my_cpu_module|GPIO6\[*\] my_cpu_module|GPIO7\[*\] my_cpu_module|GPIO8\[*\] \ my_cpu_module|GPIO9\[*\] }]] \ -to [get_registers [list {my_cpu_module|RegRes9\[*\] my_cpu_module|RegRes10\[*\]}]]
set_multicycle_path 7 -hold -end -from [get_registers [list \ {my_cpu_module|GPIO0\[*\] my_cpu_module|GPIO1\[*\] my_cpu_module|GPIO2\[*\] \ my_cpu_module|GPIO3\[*\] my_cpu_module|GPIO4\[*\] my_cpu_module|GPIO5\[*\] \ my_cpu_module|GPIO6\[*\] my_cpu_module|GPIO7\[*\] my_cpu_module|GPIO8\[*\] \ my_cpu_module|GPIO9\[*\] }]] \ -to [get_registers [list {my_cpu_module|RegRes9\[*\] my_cpu_module|RegRes10\[*\]}]] Квартус ругается, что де Warning: Ignored set_multicycle_path at DE0_PWM.SDC(141): Argument <from> is an empty collection Info: set_multicycle_path 8 -setup -end -from [get_registers [list \ {my_cpu_module|GPIO0[*] my_cpu_module|GPIO1[*] my_cpu_module|GPIO2[*] \ my_cpu_module|GPIO3[*] my_cpu_module|GPIO4[*] my_cpu_module|GPIO5[*] \ my_cpu_module|GPIO6[*] my_cpu_module|GPIO7[*] my_cpu_module|GPIO8[*] \ my_cpu_module|GPIO9[*] }]] \ -to [get_registers [list {my_cpu_module|RegRes9[*] my_cpu_module|RegRes10[*]}]] вдруг кто-то сможет меня на путь истинный наставить, помогите, пожалуйста, советом! Спасибо! ИИВ
|
|
|
|
|
Aug 7 2013, 16:18
|
Знающий
   
Группа: Участник
Сообщений: 835
Регистрация: 9-08-08
Из: Санкт-Петербург
Пользователь №: 39 515

|
Цитата(iiv @ Aug 6 2013, 23:46)  set_multicycle_path 7 -hold -end -from [get_registers [list \ {my_cpu_module|GPIO0\[*\] my_cpu_module|GPIO1\[*\] my_cpu_module|GPIO2\[*\] \ my_cpu_module|GPIO3\[*\] my_cpu_module|GPIO4\[*\] my_cpu_module|GPIO5\[*\] \ my_cpu_module|GPIO6\[*\] my_cpu_module|GPIO7\[*\] my_cpu_module|GPIO8\[*\] \ my_cpu_module|GPIO9\[*\] }]] \ -to [get_registers [list {my_cpu_module|RegRes9\[*\] my_cpu_module|RegRes10\[*\]}]][/code] Здесь ошибка в синтаксисе TCL. Во-первых, строка из фигурных скобочек передаётся в команду list, которая снова заворачивает эту строку в фигурные скобочки, создавая список в списке, и передаёт дальше в get_registers, которая резонно возвращает пустую коллекцию, так как ожидает увидеть список имён, не завёрнутый в скобочки. Во-вторых, внутри фигурных скобочек никакие подстановки не производятся, поэтому ставить обратные слэши перед квадратными скобками в этом случае не нужно. Тут надо либо убрать [list] и бэкслэши, либо убрать фигурные скобки. В среде Timequest можно посмотреть список всех активированных констрейнов с их параметрами, а также, если в TCL-консоли ввести команду puts c какой-нибудь строкой, можно посмотреть, во что эта строка на самом деле превращается. И лучше изучить TCL поближе, там есть много всего интересного. Кроме того, полезно включить в имена всех мультицикловых сигналов общие суффиксы, уникальные для каждого варианта задержки(на 2,3,4 цикла и т.д), которые и использовать в шаблоне при задании констрейнов.
|
|
|
|
|
Aug 13 2013, 08:52
|
вопрошающий
    
Группа: Свой
Сообщений: 1 726
Регистрация: 24-01-11
Пользователь №: 62 436

|
Уважаемые друзья, огромное Вам спасибо за помощь!!! Действительно было несколько багов - как заметил Timmy - баг в тикле, и баг в том, что мегафункцию я собрал с конвейером, но комментарий Fat Robotа Цитата т.е. внутри блока ваш путь будет пролегать от выхода регистра через определенное облако комбинаторной логики к входу следующего регистра. помог мне над этим задуматься. Исправив эти мои ошибки мультицикл у меня успешно заработал. Поигравшись с этим заметил одну неприятную вещь - если имеется мегафункция не только на комбинаторной логике, но и с конвейером, то поместить вызов ее в мультицикл не получается - это вроде теперь понятно, так как путь должен идти от одного регистра до другого, то есть надо использовать опции -throu. Но, чтобы ее использовать, надо знать названия промежуточных регистров. А как их узнать, если мегафункция не в сорсах, или есть более элегантный метод помещения в мультицикл конвейерной мегафункции, вдруг кто знает, скажите, пожалуйста! Спасибо ИИВ
|
|
|
|
Сообщений в этой теме
iiv Как в одном клок-домейне сказать Квартусу, что де инструкция может выполняться несколько тактов? Aug 2 2013, 19:23 Tiro Цитата(iiv @ Aug 2 2013, 22:23) Реально я... Aug 2 2013, 22:49 Джеймс Цитата(iiv @ Aug 2 2013, 23:23) Реально я... Aug 3 2013, 06:13 iosifk Цитата(Джеймс @ Aug 3 2013, 10:13) В Ваш... Aug 3 2013, 07:48    Fat Robot По сути сообщение о том, что квартус не может найт... Aug 7 2013, 09:59     iiv Уважаемые Fat Robot и Yes,
очень Вам благодарен, ... Aug 7 2013, 12:21      Fat Robot -from {port | pin | clock | instance}
Selects all ... Aug 7 2013, 12:36       iiv Цитата(Fat Robot @ Aug 7 2013, 17:36) -fr... Aug 7 2013, 12:44      Fat Robot Еще раз:
-through {port |pin | instance | net}
Se... Aug 13 2013, 10:05 yes советую констрейны (то есть то, что патерн со * чт... Aug 7 2013, 11:17
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|