Начал осваивать ПЛИСины от Хилых, конкретно - 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
/****************************************************
* 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

Знатоки, плиз хелп ме !!! Третий день бьюсь башкой об стену...