|
Непонятки с блочной двухпортовой памятью |
|
|
|
May 8 2016, 08:56
|
Частый гость
 
Группа: Свой
Сообщений: 165
Регистрация: 11-01-05
Из: Украина, г. Одесса
Пользователь №: 1 896

|
Всем доброго дня. Начал осваивать ПЛИСины от Хилых, конкретно - Artix7. Пока идет процесс набивания шишек ... Делаю модуль для приема видео-данных, который должен принимать построчно кадр информации. Входной интерфейс для модуля - AXI-Stream (8-bit), выходной интерфейс - AXI-Stream (32-bit) объединяет данные для четырех строк. Внутри использую инстанс BRAM36K в двухпортовом режиме, т.к. новая строка данных будет приниматься модулем, а ранее полученные должны при этом обрабатываться. Не возьму пока в толк что происходит в памяти: вроде бы и записываются данные, но со чтением какая-то лажа выходит...  На вход модуля в симуляторе подаю тестовый повторяющийся набор данных. С момента времени 117,6 нс симулятор начинает сыпать сообщения о коллизиях обращения к памяти. CODE `timescale 1ns / 1ps /**************************************************** * AXI4-Stream <=> BRAM 1024*32 ***************************************************/
module read_line # ( parameter LINE_SIZE = 1024, parameter ADDR_WIDTH = 10, parameter OUT_DATA_WIDTH = 32, parameter IN_DATA_WIDTH = 8 )
( input wire clk, input wire rst, // AXI input-side input wire[IN_DATA_WIDTH-1:0] in_axis_tdata, input wire in_axis_tvalid, output wire in_axis_tready, input wire in_axis_tlast, input wire in_axis_tuser, // AXI output-side output wire[OUT_DATA_WIDTH-1:0] out_axis_tdata, output wire out_axis_tvalid, input wire out_axis_tready, output wire out_axis_tlast, output wire out_axis_tuser, // test wires output reg[OUT_DATA_WIDTH-1:0] tst_wrData, output reg[IN_DATA_WIDTH-1:0] tst_rdData, output wire[3:0] tst_weA, output wire tst_writeRg, output wire tst_readRg ); //*************************************************************************** // Wire declarations //***************************************************************************
reg [ADDR_WIDTH:0] wr_ptr_reg = {ADDR_WIDTH+1{1'b0}}, wr_ptr_next; reg [ADDR_WIDTH:0] rd_ptr_reg = {ADDR_WIDTH+1{1'b0}}, rd_ptr_next; reg [31:0] in_mem_data = 32'h00000000; wire [31:0] out_mem_data, DOA;
reg [1:0] numLine = 2'b00; reg [OUT_DATA_WIDTH-1:0] out_axis_tdata_reg = {OUT_DATA_WIDTH{1'b0}}; reg out_axis_tvalid_reg = 1'b0, out_axis_tvalid_next; reg in_axis_tlast_reg = 1'b0; reg in_axis_tuser_reg = 1'b0; reg out_axis_tlast_reg = 1'b0; reg out_axis_tuser_reg = 1'b0;
// full = 1 when first MSB different but rest same wire full = ((wr_ptr_reg[ADDR_WIDTH] != rd_ptr_reg[ADDR_WIDTH]) && (wr_ptr_reg[ADDR_WIDTH-1:0] == rd_ptr_reg[ADDR_WIDTH-1:0])); // empty = 1 when pointers match exactly wire empty = wr_ptr_reg == rd_ptr_reg; // control signals reg write; reg read; wire [3:0] weA = write << numLine;
//*************************************************************************** // Code //***************************************************************************
// ========================= other modules instances ======================
// BRAM_TDP_MACRO: True Dual Port RAM // Artix-7 // Xilinx HDL Language Template, version 2015.4 ////////////////////////////////////////////////////////////////////////// // DATA_WIDTH_A/B | BRAM_SIZE | RAM Depth | ADDRA/B Width | WEA/B Width // // ===============|===========|===========|===============|=============// // 19-36 | "36Kb" | 1024 | 10-bit | 4-bit // // 10-18 | "36Kb" | 2048 | 11-bit | 2-bit // // 10-18 | "18Kb" | 1024 | 10-bit | 2-bit // // 5-9 | "36Kb" | 4096 | 12-bit | 1-bit // // 5-9 | "18Kb" | 2048 | 11-bit | 1-bit // // 3-4 | "36Kb" | 8192 | 13-bit | 1-bit // // 3-4 | "18Kb" | 4096 | 12-bit | 1-bit // // 2 | "36Kb" | 16384 | 14-bit | 1-bit // // 2 | "18Kb" | 8192 | 13-bit | 1-bit // // 1 | "36Kb" | 32768 | 15-bit | 1-bit // // 1 | "18Kb" | 16384 | 14-bit | 1-bit // //////////////////////////////////////////////////////////////////////////
BRAM_TDP_MACRO #( .BRAM_SIZE("36Kb"), // Target BRAM: "18Kb" or "36Kb" .DEVICE("7SERIES"), // Target device: "7SERIES" .DOA_REG(0), // Optional port A output register (0 or 1) .DOB_REG(0), // Optional port B output register (0 or 1) .INIT_A(36'h00000000), // Initial values on port A output port .INIT_B(36'h00000000), // Initial values on port B output port .INIT_FILE ("bram_init.vh"), .READ_WIDTH_A (32), // Valid values are 1-36 (19-36 only valid when BRAM_SIZE="36Kb") .READ_WIDTH_B (32), // Valid values are 1-36 (19-36 only valid when BRAM_SIZE="36Kb") .SIM_COLLISION_CHECK ("ALL"), // Collision check enable "ALL", "WARNING_ONLY", // "GENERATE_X_ONLY" or "NONE" .SRVAL_A(36'h00000000), // Set/Reset value for port A output .SRVAL_B(36'h00000000), // Set/Reset value for port B output .WRITE_MODE_A("WRITE_FIRST"), // "WRITE_FIRST", "READ_FIRST", or "NO_CHANGE" .WRITE_MODE_B("WRITE_FIRST"), // "WRITE_FIRST", "READ_FIRST", or "NO_CHANGE" .WRITE_WIDTH_A(32), // Valid values are 1-36 (19-36 only valid when BRAM_SIZE="36Kb") .WRITE_WIDTH_B(32) // Valid values are 1-36 (19-36 only valid when BRAM_SIZE="36Kb") //`include "bram_init.vh" ) BRAM_TDP_inst0 ( .DOA(DOA), // Output port-A data, width defined by READ_WIDTH_A parameter .DOB(out_mem_data), // Output port-B data, width defined by READ_WIDTH_B parameter .ADDRA(wr_ptr_reg[ADDR_WIDTH-1:0]), // Input port-A address, width defined by Port A depth .ADDRB(rd_ptr_reg[ADDR_WIDTH-1:0]), // Input port-B address, width defined by Port B depth .CLKA(clk), // 1-bit input port-A clock .CLKB(!clk), // 1-bit input port-B clock .DIA(in_mem_data), // Input port-A data, width defined by WRITE_WIDTH_A parameter .DIB(8'b00000000), // Input port-B data, width defined by WRITE_WIDTH_B parameter .ENA(1'b1), // 1-bit input port-A enable .ENB(1'b1), // 1-bit input port-B enable .REGCEA(1'b0), // 1-bit input port-A output register enable .REGCEB(1'b0), // 1-bit input port-B output register enable .RSTA(rst), // 1-bit input port-A reset .RSTB(rst), // 1-bit input port-B reset .WEA(weA), // Input port-A write enable, width defined by Port A depth .WEB(1'b0) // Input port-B write enable, width defined by Port B depth );
// End of BRAM_TDP_MACRO_inst instantiation
assign in_axis_tready = ~full; assign out_axis_tdata = out_axis_tdata_reg; assign out_axis_tvalid = out_axis_tvalid_reg; assign out_axis_tlast = out_axis_tlast_reg; assign out_axis_tuser = out_axis_tuser_reg;
assign tst_weA = weA; assign tst_readRg = read; assign tst_writeRg = write;
// ============== Write logic ================ always @* begin write = 1'b0;
wr_ptr_next = wr_ptr_reg;
case (numLine) 2'b00 : begin in_mem_data = {in_mem_data[31:24], in_mem_data[23:16], in_mem_data[15:8], in_axis_tdata}; end 2'b01 : begin in_mem_data = {in_mem_data[31:24], in_mem_data[23:16], in_axis_tdata, in_mem_data[7:0]}; end 2'b10 : begin in_mem_data = {in_mem_data[31:24], in_axis_tdata, in_mem_data[15:8], in_mem_data[7:0]}; end 2'b11 : begin in_mem_data = {in_axis_tdata, in_mem_data[23:16], in_mem_data[15:8], in_mem_data[7:0]}; end default: begin in_mem_data = 32'h00000000; end endcase if (in_axis_tvalid) begin // input data valid if (~full) begin // not full, perform write write = 1'b1; wr_ptr_next = wr_ptr_reg + 1; end else numLine = numLine + 1; // line full --> next line number (0..3) end end
always @(posedge clk) begin if (rst) begin wr_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; // reset wr_ptr_reg end else begin wr_ptr_reg <= wr_ptr_next; // update value wr_ptr_reg end
if (write) begin in_axis_tlast_reg <= in_axis_tlast; in_axis_tuser_reg <= in_axis_tuser; tst_wrData <= in_mem_data; end end
// ============== Read logic ================ always @* begin read = 1'b0;
rd_ptr_next = rd_ptr_reg;
out_axis_tvalid_next = out_axis_tvalid_reg;
if (out_axis_tready | ~out_axis_tvalid) begin // output data not valid OR currently being transferred if (~empty) begin // not empty, perform read read = 1'b1; out_axis_tvalid_next = 1'b1; rd_ptr_next = rd_ptr_reg + 1; end else out_axis_tvalid_next = 1'b0; end end
always @(posedge clk) begin if (rst) begin rd_ptr_reg <= {ADDR_WIDTH+1{1'b0}}; out_axis_tvalid_reg <= 1'b0; end else begin rd_ptr_reg <= rd_ptr_next; // update value rd_ptr_reg out_axis_tvalid_reg <= out_axis_tvalid_next; end if (read) begin out_axis_tlast_reg <= in_axis_tlast_reg; out_axis_tuser_reg <= in_axis_tuser_reg; tst_rdData <= out_mem_data; end end
endmodule  Знатоки, плиз хелп ме !!! Третий день бьюсь башкой об стену...
Причина редактирования: используйте тег codebox для больших сегментов кода (с) модератор
|
|
|
|
|
 |
Ответов
(1 - 6)
|
May 8 2016, 09:14
|
Гуру
     
Группа: Модераторы
Сообщений: 4 011
Регистрация: 8-09-05
Из: спб
Пользователь №: 8 369

|
Цитата(okela @ May 8 2016, 11:56)  Всем доброго дня. Начал осваивать ПЛИСины от Хилых, конкретно - Artix7. Пока идет процесс набивания шишек ... Делаю модуль для приема видео-данных, который должен принимать построчно кадр информации. Входной интерфейс для модуля - AXI-Stream (8-bit), выходной интерфейс - AXI-Stream (32-bit) объединяет данные для четырех строк. Внутри использую инстанс BRAM36K в двухпортовом режиме, т.к. новая строка данных будет приниматься модулем, а ранее полученные должны при этом обрабатываться. Не возьму пока в толк что происходит в памяти: вроде бы и записываются данные, но со чтением какая-то лажа выходит...  На вход модуля в симуляторе подаю тестовый повторяющийся набор данных. С момента времени 117,6 нс симулятор начинает сыпать сообщения о коллизиях обращения к памяти. Знатоки, плиз хелп ме !!! Третий день бьюсь башкой об стену... Я с Артиксами еще не симулировал, но думаю, что и с ними так же... Когда берете библиотечную блочную память, то она наверняка вызовет примитив, отвечающий за установление режима работы. Например у меня для RAMB16_S36.v был glbl.v... а в нем есть строки: `timescale 1 ps / 1 ps и parameter ROC_WIDTH = 100000; который показывает, сколько времени железо приходит в рабочее состояние после загрузки прошивки.... И это значит, что до 100000ps модуль блочной памяти стоит в сбросе... Поэтому я чтобы не делать "длинные" симуляции переписываю в рабочие директории МоделСима файлы исходников памяти, а в glbl.v этот параметр уменьшаю до 10... И еще приходится запускать при симуляции тестенч и одновременно glbl.v
--------------------
www.iosifk.narod.ru
|
|
|
|
|
May 8 2016, 10:22
|
Гуру
     
Группа: Модераторы
Сообщений: 4 011
Регистрация: 8-09-05
Из: спб
Пользователь №: 8 369

|
Цитата(okela @ May 8 2016, 12:36)  iosifk, спасибо за информацию. Я, признаться, даже не подумал в этом направлении копнуть... А будут проблемы, так могу по скайпу показать рабочий стол, как это делается в МоделСиме... Да и еще. У двухпортовки есть "запись вперед", а есть "чтение вперед", так что эти атрибуты тоже могут влиять... Ну и еще латентность смотрите... Удачи!
--------------------
www.iosifk.narod.ru
|
|
|
|
|
May 8 2016, 11:17
|
Профессионал
    
Группа: Свой
Сообщений: 1 214
Регистрация: 23-12-04
Пользователь №: 1 643

|
Приветствую! Зачем у Вас инверсия клока для чтения CLKB? Это так необходимо? А указатель rd_ptr_reg тактируется от clk, жуть! При этом через пол такта Вы читаете тот же адрес что и пишете отсюда и наверно коллизии доступа; Если уж делаете dual буфер так разнесите адреса для чтения и записи на размер буфера строки. Задержки в glbl на это не влияют, да и нехорошо это править для ускорения симуляции аж на целых 100 ns 8-() ! Правильнее будет сделать свой ресет на эти 100 ns. Успехов! Rob.
|
|
|
|
|
May 8 2016, 11:44
|
Знающий
   
Группа: Участник
Сообщений: 835
Регистрация: 9-08-08
Из: Санкт-Петербург
Пользователь №: 39 515

|
Цитата(okela @ May 8 2016, 11:56)  Код always @* begin case (numLine) 2'b00 : begin in_mem_data = {in_mem_data[31:24], in_mem_data[23:16], in_mem_data[15:8], in_axis_tdata}; end endcase if (in_axis_tvalid) begin // input data valid if (~full) begin // not full, perform write write = 1'b1; wr_ptr_next = wr_ptr_reg + 1; end else numLine = numLine + 1; // line full --> next line number (0..3) end end Знатоки, плиз хелп ме !!! Третий день бьюсь башкой об стену... Здесь мы видим классический комбинаторный цикл по in_mem_data, приводящий к нежданному латчу, а также бесконечный цикл по numLine=numLine+1, который приведёт к переполнению счётчика дельта циклов, когда сработает условие-детонатор (in_axis_tvalid && full), у вас просто до этого не дошло. И, кстати, in_mem_data можно гораздо проще управлять: in_mem_data_next <= in_mem_data_reg; in_mem_data_next[numLine*8+:8] <= in_axis_tdata; Дальше лень разбирать  .
|
|
|
|
|
May 9 2016, 10:01
|
Частый гость
 
Группа: Свой
Сообщений: 165
Регистрация: 11-01-05
Из: Украина, г. Одесса
Пользователь №: 1 896

|
Вроде все встало на свои места. Во-первых, увеличил интервал сброса до 200 нс, во-вторых, сделал смещение адреса чтения относительно адреса записи. Всем спасибо за подсказки !
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|