1
0
mirror of https://github.com/bmartini/zynq-axis.git synced 2024-09-05 19:19:27 +08:00

Add axis and related modules

The axis module instantiates the AXI read and write path modules. It
performs a simple stream over the AXI port interface with no error
checking. It is configured by first addressing the module and then
sending a physical memory address and the number of streaming words to
be written.

The streaming interface is not exactly like the Xilinx stream interface
in that the data is always valid when the valid flag is high, the ready
flag being low does not invalidate the data but is used to signal up
stream to stop sending data. Down stream has a buffer able to absorb to
incoming valid data until such time as up stream stop sending.
This commit is contained in:
Berin Martini 2015-01-02 15:17:31 -05:00
parent 3370f1bd32
commit d9d8540a6c
15 changed files with 3780 additions and 0 deletions

211
hdl/axis/axis.v Normal file
View File

@ -0,0 +1,211 @@
/*
* Module:
* axis
*
* Description:
* The axis module instantiates the AXI read and write path modules.
*
* Created:
* Fri Nov 7 23:07:05 EST 2014
*
* Author:
* Berin Martini (berin.martini@gmail.com)
*/
`ifndef _axis_ `define _axis_
`include "axis_write.v"
`include "axis_read.v"
module axis #(
parameter
BUF_AWIDTH = 9,
CONFIG_ID_WR = 1,
CONFIG_ID_RD = 2,
CONFIG_ADDR = 23,
CONFIG_DATA = 24,
CONFIG_AWIDTH = 5,
CONFIG_DWIDTH = 32,
STREAM_WIDTH = 32,
AXI_ID_WIDTH = 8,
AXI_LEN_WIDTH = 8,
AXI_ADDR_WIDTH = 32,
AXI_DATA_WIDTH = 256)
(input clk,
input rst,
// configuation
input [CONFIG_AWIDTH-1:0] cfg_addr,
input [CONFIG_DWIDTH-1:0] cfg_data,
input cfg_valid,
// stream interface
input wr_valid,
input [STREAM_WIDTH-1:0] wr_data,
output wr_ready,
output rd_valid,
output [STREAM_WIDTH-1:0] rd_data,
input rd_ready,
// AXI write address channel signals
input axi_awready,
output [AXI_ID_WIDTH-1:0] axi_awid,
output [AXI_ADDR_WIDTH-1:0] axi_awaddr,
output [AXI_LEN_WIDTH-1:0] axi_awlen,
output [2:0] axi_awsize,
output [1:0] axi_awburst,
output axi_awlock,
output [3:0] axi_awcache,
output [2:0] axi_awprot,
output [3:0] axi_awqos,
output axi_awvalid,
// AXI write data channel signals
input axi_wready,
output [AXI_DATA_WIDTH-1:0] axi_wdata,
output [AXI_DATA_WIDTH/8-1:0] axi_wstrb,
output axi_wlast,
output axi_wvalid,
// AXI write response channel signals
input [AXI_ID_WIDTH-1:0] axi_bid,
input [1:0] axi_bresp,
input axi_bvalid,
output axi_bready,
// AXI read address channel signals
input axi_arready,
output [AXI_ID_WIDTH-1:0] axi_arid,
output [AXI_ADDR_WIDTH-1:0] axi_araddr,
output [AXI_LEN_WIDTH-1:0] axi_arlen,
output [2:0] axi_arsize,
output [1:0] axi_arburst,
output axi_arlock,
output [3:0] axi_arcache,
output [2:0] axi_arprot,
output axi_arvalid,
output [3:0] axi_arqos,
// AXI read data channel signals
input [AXI_ID_WIDTH-1:0] axi_rid,
input [1:0] axi_rresp,
input axi_rvalid,
input [AXI_DATA_WIDTH-1:0] axi_rdata,
input axi_rlast,
output axi_rready
);
/**
* Implementation
*/
// write path static values
assign axi_awlock = 1'h0; // NORMAL_ACCESS
assign axi_awcache = 4'h0; // NON_CACHE_NON_BUFFER
assign axi_awprot = 3'h0; // DATA_SECURE_NORMAL
assign axi_awburst = 2'h1; // INCREMENTING
assign axi_awqos = 4'h0; // NOT_QOS_PARTICIPANT
assign axi_awsize = 3'h5; // THIRTY_TWO_BYTES
assign axi_wstrb = {(AXI_DATA_WIDTH/8){1'b1}};
// read path static values
assign axi_arlock = 1'h0; // NORMAL_ACCESS
assign axi_arcache = 4'h0; // NON_CACHE_NON_BUFFER
assign axi_arprot = 3'h0; // DATA_SECURE_NORMAL
assign axi_arburst = 2'h1; // INCREMENTING
assign axi_arqos = 4'h0; // NOT_QOS_PARTICIPANT
assign axi_arsize = 3'h5; // THIRTY_TWO_BYTES
// assume that all writes are successful and therefore do not need to
// check the write response
assign axi_bready = 1'b1;
axis_write #(
.BUF_AWIDTH (BUF_AWIDTH),
.CONFIG_ID (CONFIG_ID_WR),
.CONFIG_ADDR (CONFIG_ADDR),
.CONFIG_DATA (CONFIG_DATA),
.CONFIG_AWIDTH (CONFIG_AWIDTH),
.CONFIG_DWIDTH (CONFIG_DWIDTH),
.AXI_ID_WIDTH (AXI_ID_WIDTH),
.AXI_LEN_WIDTH (AXI_LEN_WIDTH),
.AXI_ADDR_WIDTH (AXI_ADDR_WIDTH),
.AXI_DATA_WIDTH (AXI_DATA_WIDTH),
.DATA_WIDTH (STREAM_WIDTH))
axis_write_ (
.clk (clk),
.rst (rst),
.cfg_addr (cfg_addr),
.cfg_data (cfg_data),
.cfg_valid (cfg_valid),
.axi_awready (axi_awready),
.axi_awid (axi_awid),
.axi_awaddr (axi_awaddr),
.axi_awlen (axi_awlen),
.axi_awvalid (axi_awvalid),
.axi_wlast (axi_wlast),
.axi_wdata (axi_wdata),
.axi_wvalid (axi_wvalid),
.axi_wready (axi_wready),
.data (wr_data),
.valid (wr_valid),
.ready (wr_ready)
);
axis_read #(
.BUF_AWIDTH (BUF_AWIDTH),
.CONFIG_ID (CONFIG_ID_RD),
.CONFIG_ADDR (CONFIG_ADDR),
.CONFIG_DATA (CONFIG_DATA),
.CONFIG_AWIDTH (CONFIG_AWIDTH),
.CONFIG_DWIDTH (CONFIG_DWIDTH),
.AXI_ID_WIDTH (AXI_ID_WIDTH),
.AXI_LEN_WIDTH (AXI_LEN_WIDTH),
.AXI_ADDR_WIDTH (AXI_ADDR_WIDTH),
.AXI_DATA_WIDTH (AXI_DATA_WIDTH),
.DATA_WIDTH (STREAM_WIDTH))
axis_read_ (
.clk (clk),
.rst (rst),
.cfg_addr (cfg_addr),
.cfg_data (cfg_data),
.cfg_valid (cfg_valid),
.axi_arready (axi_arready),
.axi_arid (axi_arid),
.axi_araddr (axi_araddr),
.axi_arlen (axi_arlen),
.axi_arvalid (axi_arvalid),
.axi_rresp (axi_rresp),
.axi_rlast (axi_rlast),
.axi_rdata (axi_rdata),
.axi_rvalid (axi_rvalid),
.axi_rready (axi_rready),
.data (rd_data),
.valid (rd_valid),
.ready (rd_ready)
);
endmodule
`endif // `ifndef _axis_

213
hdl/axis/axis_addr.v Normal file
View File

@ -0,0 +1,213 @@
/**
* Module:
* axis_addr
*
* Description:
* The axis_addr handles the AXI write address channel.
*
* Test bench:
* axis_addr_tb.v
*
* Created:
* Wed Nov 5 21:15:56 EST 2014
*
* Author:
* Berin Martini (berin.martini@gmail.com)
*/
`ifndef _axis_addr_ `define _axis_addr_
module axis_addr
#(parameter
CONFIG_DWIDTH = 32,
WIDTH_RATIO = 16,
CONVERT_SHIFT = 3,
AXI_ID_WIDTH = 8,
AXI_LEN_WIDTH = 8,
AXI_ADDR_WIDTH = 32)
(input clk,
input rst,
input [CONFIG_DWIDTH-1:0] cfg_address,
input [CONFIG_DWIDTH-1:0] cfg_length,
input cfg_valid,
output cfg_ready,
input axi_aready,
output reg [AXI_ID_WIDTH-1:0] axi_aid,
output [AXI_ADDR_WIDTH-1:0] axi_aaddr,
output [AXI_LEN_WIDTH-1:0] axi_alen,
output axi_avalid
);
/**
* Local parameters
*/
localparam BURST_NB_WIDTH = CONFIG_DWIDTH-AXI_LEN_WIDTH;
localparam BURST_LENGTH = 1<<AXI_LEN_WIDTH;
localparam
IDLE = 0,
SETUP = 1,
BURST = 2,
LAST = 3,
DONE = 4;
`ifdef VERBOSE
initial $display("\using 'axis_addr'\n");
`endif
/**
* Internal signals
*/
reg [4:0] state;
reg [4:0] state_nx;
reg last_en;
reg [AXI_LEN_WIDTH-1:0] last_nb;
reg burst_en;
reg [BURST_NB_WIDTH-1:0] burst_nb;
reg [BURST_NB_WIDTH-1:0] burst_cnt;
reg [CONFIG_DWIDTH-1:0] cfg_length_r;
reg cfg_valid_r;
reg cfg_done;
reg [CONFIG_DWIDTH-1:0] axi_address;
/**
* Implementation
*/
assign cfg_ready = state[IDLE];
assign axi_aaddr = axi_address;
assign axi_alen = state[BURST] ? (BURST_LENGTH-1) : last_nb;
assign axi_avalid = state[BURST] | state[LAST];
assign burst_done = (burst_nb == burst_cnt);
always @(posedge clk)
if (rst) cfg_valid_r <= 1'b0;
else cfg_valid_r <= cfg_valid;
always @(posedge clk)
if (cfg_valid) begin
// the shift converts from number of stream elements to number of
// bursts to be sent to the memory after stream is packed. adding a
// bit to the length ensures that the shift rounds up
cfg_length_r <= (cfg_length+WIDTH_RATIO-1) >> CONVERT_SHIFT;
end
always @(posedge clk)
if (rst) cfg_done <= 1'b0;
else cfg_done <= cfg_valid_r;
always @(posedge clk)
if (cfg_valid_r) begin
last_en <= |(cfg_length_r[AXI_LEN_WIDTH-1:0]);
last_nb <= cfg_length_r[AXI_LEN_WIDTH-1:0]-1;
burst_en <= |(cfg_length_r[AXI_LEN_WIDTH +: BURST_NB_WIDTH]);
burst_nb <= cfg_length_r[AXI_LEN_WIDTH +: BURST_NB_WIDTH]-1;
end
always @(posedge clk)
if (cfg_valid) begin
axi_address <= cfg_address;
end
else if (axi_aready & state[BURST]) begin
// each burst has 256 long words and each long word has 32 bytes
axi_address <= axi_address + (BURST_LENGTH * 32);
end
always @(posedge clk)
if (state[IDLE]) begin
burst_cnt <= 'b0;
end
else if (axi_aready & state[BURST]) begin
burst_cnt <= burst_cnt + 1;
end
always @(posedge clk)
if (rst) begin
state <= 'b0;
state[IDLE] <= 1'b1;
end
else state <= state_nx;
always @* begin : ADDR_
state_nx <= 'b0;
case (1'b1)
state[IDLE] : begin
if (cfg_valid) begin
state_nx[SETUP] <= 1'b1;
end
else state_nx[IDLE] <= 1'b1;
end
state[SETUP] : begin
if (cfg_done & burst_en) begin
state_nx[BURST] <= 1'b1;
end
else if (cfg_done & ~burst_en) begin
state_nx[LAST] <= 1'b1;
end
else state_nx[SETUP] <= 1'b1;
end
state[BURST] : begin
if (axi_aready & burst_done & last_en) begin
state_nx[LAST] <= 1'b1;
end
else if (axi_aready & burst_done & ~last_en) begin
state_nx[DONE] <= 1'b1;
end
else state_nx[BURST] <= 1'b1;
end
state[LAST] : begin
if (axi_aready) begin
state_nx[DONE] <= 1'b1;
end
else state_nx[LAST] <= 1'b1;
end
state[DONE] : begin
state_nx[IDLE] <= 1'b1;
end
default : begin
state_nx[IDLE] <= 1'b1;
end
endcase
end
always @(posedge clk)
if (state[IDLE]) begin
axi_aid <= 'b0;
end
else if (axi_aready & axi_avalid) begin
axi_aid <= axi_aid + 1;
end
endmodule
`endif // `ifndef _axis_addr_

229
hdl/axis/axis_addr_tb.v Normal file
View File

@ -0,0 +1,229 @@
/**
* Testbench:
* axis_addr
*
* Created:
* Wed Nov 5 21:16:08 EST 2014
*
* Author:
* Berin Martini (berin.martini@gmail.com)
*/
`timescale 1ns/10ps
`define TB_VERBOSE
//`define VERBOSE
`include "axis_addr.v"
module axis_addr_tb;
/**
* Clock and control functions
*/
// Generate a clk
reg clk;
always #1 clk = !clk;
// End of simulation event definition
event end_trigger;
always @(end_trigger) $finish;
`ifdef TB_VERBOSE
// Display header information
initial #1 display_header();
always @(end_trigger) display_header();
// And strobe signals at each clk
always @(posedge clk) display_signals();
`endif
// initial begin
// $dumpfile("result.vcd"); // Waveform file
// $dumpvars;
// end
/**
* Local parameters
*/
localparam CONFIG_DWIDTH = 32;
localparam WIDTH_RATIO = 16;
localparam AXI_ID_WIDTH = 8;
localparam AXI_LEN_WIDTH = 8;
localparam AXI_ADDR_WIDTH = 32;
`ifdef TB_VERBOSE
initial $display("Testbench for unit 'axis_addr'");
`endif
/**
* signals, registers and wires
*/
reg rst;
reg [CONFIG_DWIDTH-1:0] cfg_address;
reg [CONFIG_DWIDTH-1:0] cfg_length;
reg cfg_valid;
wire cfg_ready;
reg axi_aready;
wire [AXI_ID_WIDTH-1:0] axi_aid;
wire [AXI_ADDR_WIDTH-1:0] axi_aaddr;
wire [AXI_LEN_WIDTH-1:0] axi_alen;
wire axi_avalid;
/**
* Unit under test
*/
axis_addr #(
.CONFIG_DWIDTH (CONFIG_DWIDTH),
.WIDTH_RATIO (WIDTH_RATIO),
.CONVERT_SHIFT ($clog2(WIDTH_RATIO)),
.AXI_ID_WIDTH (AXI_ID_WIDTH),
.AXI_LEN_WIDTH (AXI_LEN_WIDTH),
.AXI_ADDR_WIDTH (AXI_ADDR_WIDTH))
uut (
.clk (clk),
.rst (rst),
.cfg_address (cfg_address),
.cfg_length (cfg_length),
.cfg_valid (cfg_valid),
.cfg_ready (cfg_ready),
.axi_aready (axi_aready),
.axi_aid (axi_aid),
.axi_aaddr (axi_aaddr),
.axi_alen (axi_alen),
.axi_avalid (axi_avalid)
);
/**
* Wave form display
*/
task display_signals;
$display(
"%d\t%d",
$time, rst,
"\t%d\t%d\t%b\t%b",
cfg_address,
cfg_length,
cfg_valid,
cfg_ready,
"\t%d\t%d\t%d\t%b\t%b",
axi_aid,
axi_aaddr,
axi_alen,
axi_avalid,
axi_aready,
"\tl_en %d\tl_nb %d",
uut.last_en,
uut.last_nb,
"\tb_en %d\tb_nb %d",
uut.burst_en,
uut.burst_nb,
"\t%b",
uut.state,
);
endtask // display_signals
task display_header;
$display(
"\t\ttime\trst",
"\t\tc_a",
"\t\tc_l",
"\tc_v",
"\tc_r",
"\ta_id",
"\t\ta_a",
"\ta_l",
"\ta_v",
"\ta_r",
);
endtask
/**
* Testbench program
*/
initial begin
// init values
clk = 0;
rst = 0;
cfg_address = 'b0;
cfg_length = 'b0;
cfg_valid = 'b0;
axi_aready = 'b0;
//end init
`ifdef TB_VERBOSE
$display("RESET");
`endif
repeat(6) @(negedge clk);
rst <= 1'b1;
repeat(6) @(negedge clk);
rst <= 1'b0;
@(negedge clk);
`ifdef TB_VERBOSE
$display("send config address and length");
`endif
repeat(5) @(negedge clk);
cfg_address <= 255;
//cfg_length <= 8;
cfg_length <= 256+256+64;
cfg_valid <= 1'b1;
@(negedge clk);
cfg_address <= 'b0;
cfg_length <= 'b0;
cfg_valid <= 1'b0;
repeat(5) @(negedge clk);
`ifdef TB_VERBOSE
$display("test write address channel");
`endif
repeat(3) @(negedge clk);
axi_aready <= 1'b1;
@(negedge clk);
axi_aready <= 1'b0;
@(negedge clk);
axi_aready <= 1'b1;
repeat(50) @(negedge clk);
`ifdef TB_VERBOSE
$display("END");
`endif
-> end_trigger;
end
endmodule

View File

@ -0,0 +1,105 @@
/**
* Module:
* axis_deserializer
*
* Description:
* Deserializes multiple 'up' flow bus data words into a single, larger
* 'down' data words. Arranges them first to the right and moving left.
*
* If there is a pause in the incoming 'up' stream the values already written
* into the larger 'down' word will stay until enough 'up' data has been sent
* in to complete the 'down' word unless a 'up_last' signal forces the down
* transfer. The module will stall when the down_ready flag deasserts.
*
* Testbench:
* axis_deserializer_tb.v
*
* Created:
* Thu Nov 6 17:29:58 EST 2014
*
* Authors:
* Berin Martini (berin.martini@gmail.com)
*/
`ifndef _axis_deserializer_ `define _axis_deserializer_
module axis_deserializer
#(parameter
DATA_NB = 2,
DATA_WIDTH = 8)
(input clk,
input rst,
output up_ready,
input up_valid,
input [DATA_WIDTH-1:0] up_data,
input up_last,
input down_ready,
output reg down_valid,
output reg [(DATA_WIDTH*DATA_NB)-1:0] down_data,
output reg down_last
);
/**
* Local parameters
*/
`ifdef VERBOSE
initial $display("\using 'axis_deserializer' with %0d words\n", DATA_NB);
`endif
/**
* Internal signals
*/
genvar ii;
reg [DATA_NB-1:0] token;
/**
* Implementation
*/
assign up_ready = down_ready;
always @(posedge clk)
if (rst) down_last <= 1'b0;
else if (down_ready) down_last <= up_last;
always @(posedge clk)
if (rst) down_valid <= 1'b0;
else if (down_ready) begin
down_valid <= (token[DATA_NB-1] & up_valid) | up_last;
end
always @(posedge clk)
if (rst | (down_ready & up_last)) token <= 'b1;
else if (down_ready & up_valid) begin
token <= {token, token[DATA_NB-1]};
end
generate
for (ii=0; ii<DATA_NB; ii=ii+1) begin : CONCAT_
always @(posedge clk)
if (down_ready & token[ii]) begin
down_data[ii*DATA_WIDTH +: DATA_WIDTH] <= up_data;
end
end
endgenerate
endmodule
`endif // `ifndef _axis_deserializer_

View File

@ -0,0 +1,261 @@
/**
* Testbench for:
* axis_deserializer
*
* Created:
* Thu Nov 6 17:29:45 EST 2014
*
* Author:
* Berin Martini (berin.martini@gmail.com)
*/
`timescale 1ns/10ps
`define TB_VERBOSE
//`define VERBOSE
`include "axis_deserializer.v"
module axis_deserializer_tb;
/**
* Clock and control functions
*/
// Generate a clk
reg clk;
always #1 clk = !clk;
// End of simulation event definition
event end_trigger;
always @(end_trigger) $finish;
`ifdef TB_VERBOSE
// Display header information
initial #1 display_header();
always @(end_trigger) display_header();
// And strobe signals at each clk
always @(posedge clk) display_signals();
`endif
// initial begin
// $dumpfile("result.vcd"); // Waveform file
// $dumpvars;
// end
/**
* Local parameters
*/
localparam DATA_NB = 3;
localparam DATA_WIDTH = 8;
localparam STREAM_LENGTH = 256;
`ifdef TB_VERBOSE
initial begin
$display("Testbench for unit 'axis_deserializer' data width: %d, nb: %d",
DATA_WIDTH, DATA_NB);
end
`endif
/**
* signals, registers and wires
*/
reg rst;
reg [DATA_WIDTH-1:0] up_data;
reg up_valid;
wire up_ready;
reg up_last;
wire [DATA_NB*DATA_WIDTH-1:0] down_data;
wire down_valid;
reg down_ready;
wire down_last;
reg [DATA_WIDTH-1:0] stream [0:STREAM_LENGTH-1];
integer cnt;
/**
* Unit under test
*/
axis_deserializer #(
.DATA_NB (DATA_NB),
.DATA_WIDTH (DATA_WIDTH))
uut (
.clk (clk),
.rst (rst),
.up_data (up_data),
.up_valid (up_valid),
.up_ready (up_ready),
.up_last (up_last),
.down_data (down_data),
.down_valid (down_valid),
.down_ready (down_ready),
.down_last (down_last)
);
/**
* Wave form display
*/
task display_signals;
$display(
"%d\t%d",
$time, rst,
"\t%x\t%b\t%b\t%b",
up_data,
up_valid,
up_ready,
up_last,
"\t%x\t%b\t%b\t%b",
down_data,
down_valid,
down_ready,
down_last,
);
endtask // display_signals
task display_header;
$display(
"\t\ttime\trst",
"\tu_d",
"\tu_v",
"\tu_r",
"\tu_l",
"\td_d",
"\td_v",
"\td_r",
"\td_l",
);
endtask
/**
* Testbench program
*/
always @(posedge clk)
if (up_ready) begin
cnt <= cnt + 1;
end
always @(posedge clk)
up_data <= stream[cnt];
always @(posedge clk) begin
up_valid <= 1'b0;
if (up_ready) begin
up_valid <= 1'b1;
end
end
initial begin
// init values
clk = 0;
rst = 0;
up_last = 'b0;
down_ready = 'b0;
cnt = 0;
repeat (STREAM_LENGTH) begin
stream[cnt] = cnt + 1;
cnt = cnt + 1;
end
cnt = 0;
//end init
`ifdef TB_VERBOSE
$display("RESET");
`endif
repeat(6) @(negedge clk);
rst <= 1'b1;
repeat(6) @(negedge clk);
rst <= 1'b0;
repeat(6) @(negedge clk);
`ifdef TB_VERBOSE
$display("test continuous ready");
`endif
down_ready <= 1'b1;
repeat(20) @(negedge clk);
down_ready <= 1'b0;
repeat(10) @(negedge clk);
`ifdef TB_VERBOSE
$display("test non-continuous ready");
`endif
down_ready <= 1'b1;
repeat(20) @(negedge clk);
down_ready <= 1'b0;
@(negedge clk);
down_ready <= 1'b1;
repeat(5) @(negedge clk);
down_ready <= 1'b0;
repeat(5) @(negedge clk);
down_ready <= 1'b1;
repeat(5) @(negedge clk);
down_ready <= 1'b0;
repeat(10) @(negedge clk);
down_ready <= 1'b1;
@(negedge clk);
down_ready <= 1'b0;
repeat(10) @(negedge clk);
`ifdef TB_VERBOSE
$display("test continuous ready with unalined last");
`endif
cnt <= 0;
repeat(3) @(negedge clk);
down_ready <= 1'b1;
//repeat(15) @(negedge clk);
repeat(16) @(negedge clk);
//repeat(17) @(negedge clk);
//repeat(18) @(negedge clk);
up_last <= 1'b1;
@(negedge clk);
up_last <= 1'b0;
repeat(5) @(negedge clk);
down_ready <= 1'b0;
repeat(10) @(negedge clk);
`ifdef TB_VERBOSE
$display("END");
`endif
-> end_trigger;
end
endmodule

255
hdl/axis/axis_read.v Normal file
View File

@ -0,0 +1,255 @@
/**
* Module:
* axis_read
*
* Description:
* The axis_read is configured to read an area of memory and then converts
* the large words that are returned by the AXI into a system stream.
*
* Test bench:
* axis_write_tb.v
*
* Created:
* Fri Nov 7 17:23:00 EST 2014
*
* Author:
* Berin Martini (berin.martini@gmail.com)
*/
`ifndef _axis_read_ `define _axis_read_
`include "axis_addr.v"
`include "axis_read_data.v"
module axis_read
#(parameter
BUF_AWIDTH = 9,
CONFIG_ID = 1,
CONFIG_ADDR = 23,
CONFIG_DATA = 24,
CONFIG_AWIDTH = 5,
CONFIG_DWIDTH = 32,
AXI_ID_WIDTH = 8,
AXI_LEN_WIDTH = 8,
AXI_ADDR_WIDTH = 32,
AXI_DATA_WIDTH = 32,
DATA_WIDTH = 32)
(input clk,
input rst,
input [CONFIG_AWIDTH-1:0] cfg_addr,
input [CONFIG_DWIDTH-1:0] cfg_data,
input cfg_valid,
input axi_arready,
output [AXI_ID_WIDTH-1:0] axi_arid,
output [AXI_ADDR_WIDTH-1:0] axi_araddr,
output [AXI_LEN_WIDTH-1:0] axi_arlen,
output axi_arvalid,
input axi_rresp,
input axi_rlast,
input [AXI_DATA_WIDTH-1:0] axi_rdata,
input axi_rvalid,
output axi_rready,
output [DATA_WIDTH-1:0] data,
output valid,
input ready
);
/**
* Local parameters
*/
localparam WIDTH_RATIO = AXI_DATA_WIDTH/DATA_WIDTH;
localparam CONFIG_NB = 2;
localparam
C_IDLE = 0,
C_CONFIG = 1,
C_WAIT = 2,
C_ENABLE = 3;
`ifdef VERBOSE
initial $display("\using 'axis_read'\n");
`endif
/**
* Internal signals
*/
reg [3:0] c_state;
reg [3:0] c_state_nx;
wire cfg_addr_ready;
wire cfg_data_ready;
reg [CONFIG_AWIDTH-1:0] cfg_addr_r;
reg [CONFIG_DWIDTH-1:0] cfg_data_r;
reg cfg_valid_r;
reg [CONFIG_DWIDTH*CONFIG_NB-1:0] cfg_store;
reg [7:0] cfg_cnt;
wire [CONFIG_DWIDTH-1:0] start_addr;
reg [CONFIG_DWIDTH-1:0] cfg_address;
wire [CONFIG_DWIDTH-1:0] str_length;
reg [CONFIG_DWIDTH-1:0] cfg_length;
reg cfg_enable;
/**
* Implementation
*/
assign start_addr = cfg_store[CONFIG_DWIDTH +: CONFIG_DWIDTH];
assign str_length = cfg_store[0 +: CONFIG_DWIDTH];
assign id_valid = (CONFIG_ID == cfg_data_r);
assign addressed = (CONFIG_ADDR == cfg_addr_r) & cfg_valid_r;
assign axis_data = (CONFIG_DATA == cfg_addr_r) & cfg_valid_r;
// register for improved timing
always @(posedge clk)
if (rst) cfg_valid_r <= 1'b0;
else cfg_valid_r <= cfg_valid;
// register for improved timing
always @(posedge clk) begin
cfg_addr_r <= cfg_addr;
cfg_data_r <= cfg_data;
end
always @(posedge clk)
if (c_state[C_CONFIG] & axis_data) begin
cfg_store <= {cfg_store, cfg_data_r};
end
always @(posedge clk) begin
cfg_cnt <= 'b0;
if (c_state[C_CONFIG]) begin
cfg_cnt <= cfg_cnt + axis_data;
end
end
always @(posedge clk)
if (rst) cfg_enable <= 1'b0;
else cfg_enable <= c_state[C_ENABLE];
always @(posedge clk) begin
if (c_state[C_ENABLE]) begin
cfg_address <= start_addr;
cfg_length <= str_length;
end
end
always @(posedge clk)
if (rst) begin
c_state <= 'b0;
c_state[C_IDLE] <= 1'b1;
end
else c_state <= c_state_nx;
always @* begin : CONFIG_
c_state_nx = 'b0;
case (1'b1)
c_state[C_IDLE] : begin
if (addressed & id_valid) begin
c_state_nx[C_CONFIG] = 1'b1;
end
else c_state_nx[C_IDLE] = 1'b1;
end
c_state[C_CONFIG] : begin
if (axis_data & ((CONFIG_NB-1) <= cfg_cnt)) begin
c_state_nx[C_WAIT] = 1'b1;
end
else c_state_nx[C_CONFIG] = 1'b1;
end
c_state[C_WAIT] : begin
if (cfg_addr_ready & cfg_data_ready) begin
c_state_nx[C_ENABLE] = 1'b1;
end
else c_state_nx[C_WAIT] = 1'b1;
end
c_state[C_ENABLE] : begin
c_state_nx[C_IDLE] = 1'b1;
end
default : begin
c_state_nx[C_IDLE] = 1'b1;
end
endcase
end
axis_addr #(
.CONFIG_DWIDTH (CONFIG_DWIDTH),
.WIDTH_RATIO (WIDTH_RATIO),
.CONVERT_SHIFT ($clog2(WIDTH_RATIO)),
.AXI_ID_WIDTH (AXI_ID_WIDTH),
.AXI_LEN_WIDTH (AXI_LEN_WIDTH),
.AXI_ADDR_WIDTH (AXI_ADDR_WIDTH))
axis_addr_ (
.clk (clk),
.rst (rst),
.cfg_address (cfg_address),
.cfg_length (cfg_length),
.cfg_valid (cfg_enable),
.cfg_ready (cfg_addr_ready),
.axi_aready (axi_arready),
.axi_aid (axi_arid),
.axi_aaddr (axi_araddr),
.axi_alen (axi_arlen),
.axi_avalid (axi_arvalid)
);
axis_read_data #(
.BUF_AWIDTH (BUF_AWIDTH),
.CONFIG_DWIDTH (CONFIG_DWIDTH),
.WIDTH_RATIO (WIDTH_RATIO),
.AXI_DATA_WIDTH (AXI_DATA_WIDTH),
.DATA_WIDTH (DATA_WIDTH))
axis_read_data_ (
.clk (clk),
.rst (rst),
.cfg_length (cfg_length),
.cfg_valid (cfg_enable),
.cfg_ready (cfg_data_ready),
.axi_rresp (axi_rresp),
.axi_rlast (axi_rlast),
.axi_rdata (axi_rdata),
.axi_rvalid (axi_rvalid),
.axi_rready (axi_rready),
.data (data),
.valid (valid),
.ready (ready)
);
endmodule
`endif // `ifndef _axis_read_

184
hdl/axis/axis_read_data.v Normal file
View File

@ -0,0 +1,184 @@
/**
* Module:
* axis_read_data
*
* Description:
* The axis_read_data handles the AXI read data channel.
*
* Testbench:
* axis_read_data_tb.v
*
* Created:
* Tue Nov 4 22:18:14 EST 2014
*
* Author:
* Berin Martini (berin.martini@gmail.com)
*/
`ifndef _axis_read_data_ `define _axis_read_data_
`include "fifo_simple.v"
`include "axis_serializer.v"
module axis_read_data
#(parameter
BUF_AWIDTH = 9,
CONFIG_DWIDTH = 32,
WIDTH_RATIO = 16,
AXI_DATA_WIDTH = 32,
DATA_WIDTH = 32)
(input clk,
input rst,
input [CONFIG_DWIDTH-1:0] cfg_length,
input cfg_valid,
output cfg_ready,
input axi_rresp,
input axi_rlast,
input [AXI_DATA_WIDTH-1:0] axi_rdata,
input axi_rvalid,
output axi_rready,
output [DATA_WIDTH-1:0] data,
output reg valid,
input ready
);
/**
* Local parameters
*/
localparam
IDLE = 0,
ACTIVE = 1,
WAIT = 2,
DONE = 3;
`ifdef VERBOSE
initial $display("\using 'axis_read_data'\n");
`endif
/**
* Internal signals
*/
reg [3:0] state;
reg [3:0] state_nx;
reg [CONFIG_DWIDTH-1:0] str_cnt;
reg [CONFIG_DWIDTH-1:0] str_length;
wire buf_pop;
wire buf_full;
wire buf_empty;
wire [DATA_WIDTH-1:0] buf_data;
wire buf_valid;
/**
* Implementation
*/
assign cfg_ready = state[IDLE];
assign buf_pop = ~buf_empty & ready;
always @(posedge clk)
if (cfg_valid) begin
str_length <= cfg_length-1;
end
always @(posedge clk)
if (state[IDLE]) str_cnt <= 'b0;
else str_cnt <= str_cnt + buf_pop;
always @(posedge clk)
if (rst) valid <= 1'b0;
else valid <= buf_pop & state[ACTIVE];
fifo_simple #(
.DATA_WIDTH (DATA_WIDTH),
.ADDR_WIDTH (BUF_AWIDTH))
buffer_ (
.clk (clk),
.rst (state[IDLE]),
.count (),
.empty (buf_empty),
.empty_a (),
.full (buf_full),
.full_a (),
.push_data (buf_data),
.push (buf_valid),
.pop_data (data),
.pop (buf_pop)
);
axis_serializer #(
.DATA_NB (WIDTH_RATIO),
.DATA_WIDTH (DATA_WIDTH))
serializer_ (
.clk (clk),
.rst (state[IDLE]),
.up_data (axi_rdata),
.up_valid (axi_rvalid),
.up_ready (axi_rready),
.down_data (buf_data),
.down_valid (buf_valid),
.down_ready ( ~buf_full)
);
always @(posedge clk)
if (rst) begin
state <= 'b0;
state[IDLE] <= 1'b1;
end
else state <= state_nx;
always @* begin : DATA_
state_nx <= 'b0;
case (1'b1)
state[IDLE] : begin
if (cfg_valid) begin
state_nx[ACTIVE] <= 1'b1;
end
else state_nx[IDLE] <= 1'b1;
end
state[ACTIVE] : begin
if (buf_pop & (str_length == str_cnt)) begin
state_nx[WAIT] <= 1'b1;
end
else state_nx[ACTIVE] <= 1'b1;
end
state[WAIT] : begin
state_nx[DONE] <= 1'b1;
end
state[DONE] : begin
state_nx[IDLE] <= 1'b1;
end
default : begin
state_nx[IDLE] <= 1'b1;
end
endcase
end
endmodule
`endif // `ifndef _axis_read_data_

View File

@ -0,0 +1,240 @@
/**
* Testbench:
* axis_read_data
*
* Created:
* Tue Nov 4 22:17:15 EST 2014
*
* Author:
* Berin Martini (berin.martini@gmail.com)
*/
`timescale 1ns/10ps
`define TB_VERBOSE
//`define VERBOSE
`include "axis_read_data.v"
module axis_read_data_tb;
/**
* Clock and control functions
*/
// Generate a clk
reg clk;
always #1 clk = !clk;
// End of simulation event definition
event end_trigger;
always @(end_trigger) $finish;
`ifdef TB_VERBOSE
// Display header information
initial #1 display_header();
always @(end_trigger) display_header();
// And strobe signals at each clk
always @(posedge clk) display_signals();
`endif
// initial begin
// $dumpfile("result.vcd"); // Waveform file
// $dumpvars;
// end
/**
* Local parameters
*/
localparam STREAM_LENGTH = (256*8*2)-4;
localparam BUF_AWIDTH = 4;
localparam CONFIG_DWIDTH = 32;
localparam WIDTH_RATIO = 8;
localparam AXI_DATA_WIDTH = 256;
localparam DATA_WIDTH = 32;
`ifdef TB_VERBOSE
initial $display("Testbench for unit 'axis_read_data'");
`endif
/**
* signals, registers and wires
*/
reg rst;
reg [CONFIG_DWIDTH-1:0] cfg_length;
reg cfg_valid;
wire cfg_ready;
reg axi_rresp;
reg axi_rlast;
reg [AXI_DATA_WIDTH-1:0] axi_rdata;
reg axi_rvalid;
wire axi_rready;
wire [DATA_WIDTH-1:0] data;
wire valid;
reg ready;
/**
* Unit under test
*/
axis_read_data #(
.BUF_AWIDTH (BUF_AWIDTH),
.CONFIG_DWIDTH (CONFIG_DWIDTH),
.WIDTH_RATIO (WIDTH_RATIO),
.AXI_DATA_WIDTH (AXI_DATA_WIDTH),
.DATA_WIDTH (DATA_WIDTH))
uut (
.clk (clk),
.rst (rst),
.cfg_length (cfg_length),
.cfg_valid (cfg_valid),
.cfg_ready (cfg_ready),
.axi_rresp (axi_rresp),
.axi_rlast (axi_rlast),
.axi_rdata (axi_rdata),
.axi_rvalid (axi_rvalid),
.axi_rready (axi_rready),
.data (data),
.valid (valid),
.ready (ready)
);
/**
* Wave form display
*/
task display_signals;
$display(
"%d\t%d",
$time, rst,
"\t%d\t%b",
cfg_length,
cfg_valid,
cfg_ready,
"\t%x\t%b\t%b",
axi_rdata,
axi_rvalid,
axi_rready,
"\t%x\t%b\t%b",
data,
valid,
ready,
"\t%b",
uut.state,
);
endtask // display_signals
task display_header;
$display(
"\t\ttime\trst",
"\t\tc_l",
"\tc_v",
"\tc_r",
"\t\t\t\t\t\tr_d",
"\t\t\tr_v",
"\tr_r",
"\t\ts_d",
"\ts_v",
"\ts_r",
);
endtask
/**
* Testbench program
*/
initial begin
// init values
clk = 0;
rst = 0;
cfg_length = 'b0;
cfg_valid = 'b0;
axi_rresp = 'b0;
axi_rlast = 'b0;
axi_rdata = 'b0;
axi_rvalid = 'b0;
ready = 'b0;
//end init
`ifdef TB_VERBOSE
$display("RESET");
`endif
repeat(6) @(negedge clk);
rst <= 1'b1;
repeat(6) @(negedge clk);
rst <= 1'b0;
@(negedge clk);
`ifdef TB_VERBOSE
$display("send config id, start address and length");
`endif
repeat(5) @(negedge clk);
cfg_length <= 10;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_length <= 'b0;
cfg_valid <= 1'b0;
repeat(5) @(negedge clk);
`ifdef TB_VERBOSE
$display("test read");
`endif
ready <= 1'b1;
repeat(5) @(negedge clk);
axi_rdata <= {32'd8, 32'd7, 32'd6, 32'd5, 32'd4, 32'd3, 32'd2, 32'd1};
axi_rvalid <= 1'b1;
@(negedge clk);
axi_rdata <= {32'd9, 32'd8, 32'd7, 32'd6, 32'd5, 32'd4, 32'd3, 32'd2};
while ( ~axi_rready) @(negedge clk);
axi_rvalid <= 1'b1;
@(negedge clk);
axi_rdata <= 'b0;
axi_rvalid <= 1'b0;
repeat(15) @(negedge clk);
`ifdef TB_VERBOSE
$display("END");
`endif
-> end_trigger;
end
endmodule

439
hdl/axis/axis_read_tb.v Normal file
View File

@ -0,0 +1,439 @@
/**
* Testbench:
* axis_read
*
* Created:
* Fri Nov 7 17:22:50 EST 2014
*
* Author:
* Berin Martini (berin.martini@gmail.com)
*/
`timescale 1ns/10ps
`define TB_VERBOSE
//`define VERBOSE
`include "axis_read.v"
module axis_read_tb;
/**
* Clock and control functions
*/
// Generate a clk
reg clk;
always #1 clk = !clk;
// End of simulation event definition
event end_trigger;
always @(end_trigger) $finish;
`ifdef TB_VERBOSE
// Display header information
initial #1 display_header();
always @(end_trigger) display_header();
// And strobe signals at each clk
always @(posedge clk) display_signals();
`endif
// initial begin
// $dumpfile("result.vcd"); // Waveform file
// $dumpvars;
// end
/**
* Local parameters
*/
localparam STREAM_LENGTH = (256*8*2)-4;
localparam BUF_AWIDTH = 4;
localparam CONFIG_ID = 1;
localparam CONFIG_ADDR = 23;
localparam CONFIG_DATA = 24;
localparam CONFIG_AWIDTH = 5;
localparam CONFIG_DWIDTH = 32;
localparam AXI_ID_WIDTH = 8;
localparam AXI_ADDR_WIDTH = 32;
localparam AXI_DATA_WIDTH = 256;
localparam DATA_WIDTH = 32;
`ifdef TB_VERBOSE
initial $display("Testbench for unit 'axis_read'");
`endif
/**
* signals, registers and wires
*/
reg rst;
reg [CONFIG_AWIDTH-1:0] cfg_addr;
reg [CONFIG_DWIDTH-1:0] cfg_data;
reg cfg_valid;
reg axi_arready;
wire [AXI_ID_WIDTH-1:0] axi_arid;
wire [AXI_ADDR_WIDTH-1:0] axi_araddr;
wire [7:0] axi_arlen;
wire axi_arvalid;
reg [AXI_DATA_WIDTH-1:0] axi_rdata;
reg axi_rvalid;
wire axi_rready;
wire [DATA_WIDTH-1:0] data;
wire valid;
reg ready;
/**
* Unit under test
*/
axis_read #(
.BUF_AWIDTH (BUF_AWIDTH),
.CONFIG_ID (CONFIG_ID),
.CONFIG_ADDR (CONFIG_ADDR),
.CONFIG_DATA (CONFIG_DATA),
.CONFIG_AWIDTH (CONFIG_AWIDTH),
.CONFIG_DWIDTH (CONFIG_DWIDTH),
.AXI_ID_WIDTH (AXI_ID_WIDTH),
.AXI_ADDR_WIDTH (AXI_ADDR_WIDTH),
.AXI_DATA_WIDTH (AXI_DATA_WIDTH),
.DATA_WIDTH (DATA_WIDTH))
uut (
.clk (clk),
.rst (rst),
.cfg_addr (cfg_addr),
.cfg_data (cfg_data),
.cfg_valid (cfg_valid),
.axi_arready (axi_arready),
.axi_arid (axi_arid),
.axi_araddr (axi_araddr),
.axi_arlen (axi_arlen),
.axi_arvalid (axi_arvalid),
.axi_rresp (),
.axi_rlast (),
.axi_rdata (axi_rdata),
.axi_rvalid (axi_rvalid),
.axi_rready (axi_rready),
.data (data),
.valid (valid),
.ready (ready)
);
/**
* Wave form display
*/
task display_signals;
$display(
"%d\t%d",
$time, rst,
"\t%d\t%d\t%b",
cfg_addr,
cfg_data,
cfg_valid,
"\t%d\t%d\t%d\t%b\t%b",
axi_arid,
axi_araddr,
axi_arlen,
axi_arvalid,
axi_arready,
"\t%x\t%b\t%b",
axi_rdata,
axi_rvalid,
axi_rready,
"\t%d\t%b\t%b",
data,
valid,
ready,
"\t%b",
uut.c_state,
"\t%b",
uut.axis_addr_.state,
);
endtask // display_signals
task display_header;
$display(
"\t\ttime\trst",
"\tc_a",
"\t\tc_d",
"\tc_v",
"\tar_id",
"\t\tar_a",
"\tar_l",
"\tar_v",
"\tar_r",
"\t\t\t\t\t\tr_d",
"\t\t\t\tr_v",
"\tr_r",
"\t\ts_d",
"\ts_v",
"\tm_r",
);
endtask
/**
* Testbench program
*/
initial begin
// init values
clk = 0;
rst = 0;
cfg_addr = 'b0;
cfg_data = 'b0;
cfg_valid = 'b0;
axi_arready = 'b0;
axi_rdata = 'b0;
axi_rvalid = 'b0;
ready = 'b0;
//end init
`ifdef TB_VERBOSE
$display("RESET");
`endif
repeat(6) @(negedge clk);
rst <= 1'b1;
repeat(6) @(negedge clk);
rst <= 1'b0;
@(negedge clk);
`ifdef TB_VERBOSE
$display("send config id, start address and length");
`endif
repeat(5) @(negedge clk);
cfg_addr <= CONFIG_ADDR;
cfg_data <= CONFIG_ID;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_addr <= 'b0;
cfg_data <= 'b0;
cfg_valid <= 1'b0;
repeat(5) @(negedge clk);
// memory address
cfg_addr <= CONFIG_DATA;
cfg_data <= 4;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_addr <= 'b0;
cfg_data <= 'b0;
cfg_valid <= 1'b0;
repeat(5) @(negedge clk);
// length of flow stream
cfg_addr <= CONFIG_DATA;
cfg_data <= 8;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_addr <= 'b0;
cfg_data <= 'b0;
cfg_valid <= 1'b0;
repeat(5) @(negedge clk);
`ifdef TB_VERBOSE
$display("test read address channel");
`endif
repeat(3) @(negedge clk);
axi_arready <= 1'b1;
repeat(5) @(negedge clk);
axi_arready <= 1'b0;
`ifdef TB_VERBOSE
$display("test read data channel");
`endif
@(negedge clk);
ready <= 1'b1;
axi_rdata <= 'b0;
axi_rvalid <= 1'b0;
repeat(5) @(negedge clk);
axi_rdata <= {32'd8, 32'd7, 32'd6, 32'd5, 32'd4, 32'd3, 32'd2, 32'd1};
axi_rvalid <= 1'b1;
@(negedge clk);
axi_rvalid <= 1'b0;
repeat(15) @(negedge clk);
`ifdef TB_VERBOSE
$display("send config id, start address and length");
`endif
repeat(5) @(negedge clk);
cfg_addr <= CONFIG_ADDR;
cfg_data <= CONFIG_ID;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_addr <= CONFIG_DATA;
cfg_data <= 4;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_addr <= CONFIG_DATA;
cfg_data <= 20;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_addr <= 'b0;
cfg_data <= 'b0;
cfg_valid <= 1'b0;
repeat(5) @(negedge clk);
`ifdef TB_VERBOSE
$display("test read address channel");
`endif
repeat(3) @(negedge clk);
axi_arready <= 1'b1;
repeat(5) @(negedge clk);
`ifdef TB_VERBOSE
$display("test read data channel");
`endif
@(negedge clk);
ready <= 1'b1;
axi_rdata <= 'b0;
axi_rvalid <= 1'b0;
repeat(5) @(negedge clk);
axi_rdata <= {32'd8, 32'd7, 32'd6, 32'd5, 32'd4, 32'd3, 32'd2, 32'd1};
axi_rvalid <= 1'b1;
@(negedge clk);
axi_rdata <= {32'd16, 32'd15, 32'd14, 32'd13, 32'd12, 32'd11, 32'd10, 32'd9};
while ( ~axi_rready) @(negedge clk);
axi_rvalid <= 1'b1;
@(negedge clk);
axi_rdata <= {32'd24, 32'd23, 32'd22, 32'd21, 32'd20, 32'd19, 32'd18, 32'd17};
while ( ~axi_rready) @(negedge clk);
axi_rvalid <= 1'b1;
@(negedge clk);
axi_rdata <= 'b0;
axi_rvalid <= 1'b0;
axi_rvalid <= 1'b0;
repeat(15) @(negedge clk);
/*
`ifdef TB_VERBOSE
$display("send config id, start address and length");
`endif
repeat(5) @(negedge clk);
cfg_addr <= CONFIG_ADDR;
cfg_data <= CONFIG_ID;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_addr <= CONFIG_DATA;
cfg_data <= 255;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_addr <= CONFIG_DATA;
cfg_data <= STREAM_LENGTH;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_addr <= 'b0;
cfg_data <= 'b0;
cfg_valid <= 1'b0;
repeat(5) @(negedge clk);
`ifdef TB_VERBOSE
$display("test long read");
`endif
repeat(3) @(negedge clk);
// ready to recive address
axi_arready <= 1'b1;
repeat(5) @(negedge clk);
// ready to recive data
axi_rready <= 1'b1;
data <= 'b0;
valid <= 1'b0;
repeat(5) @(negedge clk);
repeat (STREAM_LENGTH) begin
// stream data into axis
data <= data + 1;
valid <= 1'b1;
@(negedge clk);
end
valid <= 1'b0;
repeat(15) @(negedge clk);
axi_rready <= 1'b0;
repeat(15) @(negedge clk);
*/
`ifdef TB_VERBOSE
$display("END");
`endif
-> end_trigger;
end
endmodule

117
hdl/axis/axis_serializer.v Normal file
View File

@ -0,0 +1,117 @@
/**
* Module:
* axis_serializer
*
* Description:
* Serializes the 'up' flow bus data word into multiple smaller 'down' data
* words. The module will stall when the down_ready flag deasserts.
*
* Testbench:
* axis_serializer_tb.v
*
* Created:
* Fri Nov 7 11:50:04 EST 2014
*
* Authors:
* Berin Martini (berin.martini@gmail.com)
*/
`ifndef _axis_serializer_ `define _axis_serializer_
module axis_serializer
#(parameter
DATA_NB = 2,
DATA_WIDTH = 8)
(input clk,
input rst,
output up_ready,
input up_valid,
input [(DATA_WIDTH*DATA_NB)-1:0] up_data,
input down_ready,
output reg down_valid,
output reg [DATA_WIDTH-1:0] down_data
);
/**
* Local parameters
*/
`ifdef VERBOSE
initial $display("\using 'axis_serializer' with %0d words\n", DATA_NB);
`endif
/**
* Internal signals
*/
wire [2*DATA_NB-1:0] token_nx;
reg [DATA_NB-1:0] token;
reg [DATA_NB-1:0] serial_valid;
reg [(DATA_WIDTH*DATA_NB)-1:0] serial_data;
wire serial_start;
/**
* Implementation
*/
assign up_ready = down_ready & token[0];
assign serial_start = |(token >> 1);
assign token_nx = {token, token};
always @(posedge clk)
if (rst) token <= 'b1;
else if (down_ready) begin
if (serial_start | (up_ready & up_valid)) begin
token <= token_nx[DATA_NB-1 +: DATA_NB];
end
end
always @(posedge clk)
if (down_ready) begin
serial_data <= serial_data >> DATA_WIDTH;
if (up_ready & up_valid) begin
serial_data <= up_data;
end
end
always @(posedge clk)
if (rst) serial_valid <= 'b0;
else if (down_ready) begin
serial_valid <= {serial_valid[0 +: DATA_NB-1], 1'b0};
if (up_ready & up_valid) begin
serial_valid <= {serial_valid[0 +: DATA_NB-1], 1'b1};
end
end
always @(posedge clk)
if (down_ready) begin
down_data <= serial_data[0 +: DATA_WIDTH];
end
always @(posedge clk)
if (rst) down_valid <= 1'b0;
else if (down_ready) begin
down_valid <= |(serial_valid);
end
endmodule
`endif // `ifndef _axis_serializer_

View File

@ -0,0 +1,243 @@
/**
* Testbench for:
* axis_serializer
*
* Created:
* Fri Nov 7 11:49:55 EST 2014
*
* Author:
* Berin Martini (berin.martini@gmail.com)
*/
`timescale 1ns/10ps
`define TB_VERBOSE
//`define VERBOSE
`include "axis_serializer.v"
module axis_serializer_tb;
/**
* Clock and control functions
*/
// Generate a clk
reg clk;
always #1 clk = !clk;
// End of simulation event definition
event end_trigger;
always @(end_trigger) $finish;
`ifdef TB_VERBOSE
// Display header information
initial #1 display_header();
always @(end_trigger) display_header();
// And strobe signals at each clk
always @(posedge clk) display_signals();
`endif
// initial begin
// $dumpfile("result.vcd"); // Waveform file
// $dumpvars;
// end
/**
* Local parameters
*/
localparam DATA_NB = 3;
localparam DATA_WIDTH = 8;
localparam STREAM_LENGTH = 256;
`ifdef TB_VERBOSE
initial begin
$display("Testbench for unit 'axis_serializer' data width: %d, nb: %d",
DATA_WIDTH, DATA_NB);
end
`endif
/**
* signals, registers and wires
*/
reg rst;
reg [DATA_NB*DATA_WIDTH-1:0] up_data;
wire up_valid;
wire up_ready;
wire [DATA_WIDTH-1:0] down_data;
wire down_valid;
reg down_ready;
reg [DATA_WIDTH-1:0] stream [0:STREAM_LENGTH-1];
integer cnt;
/**
* Unit under test
*/
axis_serializer #(
.DATA_NB (DATA_NB),
.DATA_WIDTH (DATA_WIDTH))
uut (
.clk (clk),
.rst (rst),
.up_data (up_data),
.up_valid (up_valid),
.up_ready (up_ready),
.down_data (down_data),
.down_valid (down_valid),
.down_ready (down_ready)
);
/**
* Wave form display
*/
task display_signals;
$display(
"%d\t%d",
$time, rst,
"\tu2: %d\tu1: %d\tu0: %d",
up_data[2*DATA_WIDTH+:DATA_WIDTH],
up_data[1*DATA_WIDTH+:DATA_WIDTH],
up_data[0*DATA_WIDTH+:DATA_WIDTH],
"\tv %b\tr %b",
up_valid,
up_ready,
"\t%d\t%b\t%b",
down_data,
down_valid,
down_ready,
"\t%b",
uut.token,
"\t%x\t%b",
uut.serial_data,
uut.serial_valid,
);
endtask // display_signals
task display_header;
$display(
"\t\ttime\trst",
"\tu2_d",
"\tu1_d",
"\tu0_d",
"\tu_v",
"\tu_r",
"\td_d",
"\td_v",
"\td_r",
);
endtask
/**
* Testbench program
*/
//assign up_valid = up_ready;
assign up_valid = 1'b1;
always @(posedge clk)
if (up_ready & up_valid) begin
cnt <= cnt + 1;
end
always @(posedge clk)
up_data <= {stream[cnt]+4'd2, stream[cnt]+4'd1, stream[cnt]};
initial begin
// init values
clk = 0;
rst = 0;
down_ready = 'b0;
cnt = 0;
repeat (STREAM_LENGTH) begin
stream[cnt] = (DATA_NB*cnt)+1;
cnt = cnt + 1;
end
cnt = 0;
//end init
`ifdef TB_VERBOSE
$display("RESET");
`endif
repeat(6) @(negedge clk);
rst <= 1'b1;
repeat(6) @(negedge clk);
rst <= 1'b0;
repeat(6) @(negedge clk);
`ifdef TB_VERBOSE
$display("test continuous ready");
`endif
@(negedge clk);
down_ready <= 1'b1;
repeat(20) @(negedge clk);
down_ready <= 1'b0;
repeat(10) @(negedge clk);
`ifdef TB_VERBOSE
$display("test non-continuous ready");
`endif
down_ready <= 1'b1;
repeat(20) @(negedge clk);
down_ready <= 1'b0;
@(negedge clk);
down_ready <= 1'b1;
repeat(5) @(negedge clk);
down_ready <= 1'b0;
repeat(5) @(negedge clk);
down_ready <= 1'b1;
repeat(5) @(negedge clk);
down_ready <= 1'b0;
repeat(10) @(negedge clk);
down_ready <= 1'b1;
@(negedge clk);
down_ready <= 1'b0;
@(negedge clk);
down_ready <= 1'b1;
@(negedge clk);
down_ready <= 1'b0;
repeat(10) @(negedge clk);
`ifdef TB_VERBOSE
$display("END");
`endif
-> end_trigger;
end
endmodule

255
hdl/axis/axis_write.v Normal file
View File

@ -0,0 +1,255 @@
/**
* Module:
* axis_write
*
* Description:
* The axis_write takes a system stream an translate it to the axi data
* channel protocol.
*
* Test bench:
* axis_write_tb.v
*
* Created:
* Tue Nov 4 22:18:14 EST 2014
*
* Author:
* Berin Martini (berin.martini@gmail.com)
*/
`ifndef _axis_write_ `define _axis_write_
`include "axis_addr.v"
`include "axis_write_data.v"
module axis_write
#(parameter
BUF_AWIDTH = 9,
CONFIG_ID = 1,
CONFIG_ADDR = 23,
CONFIG_DATA = 24,
CONFIG_AWIDTH = 5,
CONFIG_DWIDTH = 32,
AXI_ID_WIDTH = 8,
AXI_LEN_WIDTH = 8,
AXI_ADDR_WIDTH = 32,
AXI_DATA_WIDTH = 32,
DATA_WIDTH = 32)
(input clk,
input rst,
input [CONFIG_AWIDTH-1:0] cfg_addr,
input [CONFIG_DWIDTH-1:0] cfg_data,
input cfg_valid,
input axi_awready,
output [AXI_ID_WIDTH-1:0] axi_awid,
output [AXI_ADDR_WIDTH-1:0] axi_awaddr,
output [AXI_LEN_WIDTH-1:0] axi_awlen,
output axi_awvalid,
output axi_wlast,
output [AXI_DATA_WIDTH-1:0] axi_wdata,
output axi_wvalid,
input axi_wready,
input [DATA_WIDTH-1:0] data,
input valid,
output ready
);
/**
* Local parameters
*/
localparam WIDTH_RATIO = AXI_DATA_WIDTH/DATA_WIDTH;
localparam CONFIG_NB = 2;
localparam
C_IDLE = 0,
C_CONFIG = 1,
C_WAIT = 2,
C_ENABLE = 3;
`ifdef VERBOSE
initial $display("\using 'axis_write'\n");
`endif
/**
* Internal signals
*/
reg [3:0] c_state;
reg [3:0] c_state_nx;
wire cfg_addr_ready;
wire cfg_data_ready;
reg [CONFIG_AWIDTH-1:0] cfg_addr_r;
reg [CONFIG_DWIDTH-1:0] cfg_data_r;
reg cfg_valid_r;
reg [CONFIG_DWIDTH*CONFIG_NB-1:0] cfg_store;
reg [7:0] cfg_cnt;
wire [CONFIG_DWIDTH-1:0] start_addr;
reg [CONFIG_DWIDTH-1:0] cfg_address;
wire [CONFIG_DWIDTH-1:0] str_length;
reg [CONFIG_DWIDTH-1:0] cfg_length;
reg cfg_enable;
/**
* Implementation
*/
assign start_addr = cfg_store[CONFIG_DWIDTH +: CONFIG_DWIDTH];
assign str_length = cfg_store[0 +: CONFIG_DWIDTH];
assign id_valid = (CONFIG_ID == cfg_data_r);
assign addressed = (CONFIG_ADDR == cfg_addr_r) & cfg_valid_r;
assign axis_data = (CONFIG_DATA == cfg_addr_r) & cfg_valid_r;
// register for improved timing
always @(posedge clk)
if (rst) cfg_valid_r <= 1'b0;
else cfg_valid_r <= cfg_valid;
// register for improved timing
always @(posedge clk) begin
cfg_addr_r <= cfg_addr;
cfg_data_r <= cfg_data;
end
always @(posedge clk)
if (c_state[C_CONFIG] & axis_data) begin
cfg_store <= {cfg_store, cfg_data_r};
end
always @(posedge clk) begin
cfg_cnt <= 'b0;
if (c_state[C_CONFIG]) begin
cfg_cnt <= cfg_cnt + axis_data;
end
end
always @(posedge clk)
if (rst) cfg_enable <= 1'b0;
else cfg_enable <= c_state[C_ENABLE];
always @(posedge clk) begin
if (c_state[C_ENABLE]) begin
cfg_address <= start_addr;
cfg_length <= str_length;
end
end
always @(posedge clk)
if (rst) begin
c_state <= 'b0;
c_state[C_IDLE] <= 1'b1;
end
else c_state <= c_state_nx;
always @* begin : CONFIG_
c_state_nx = 'b0;
case (1'b1)
c_state[C_IDLE] : begin
if (addressed & id_valid) begin
c_state_nx[C_CONFIG] = 1'b1;
end
else c_state_nx[C_IDLE] = 1'b1;
end
c_state[C_CONFIG] : begin
if (axis_data & ((CONFIG_NB-1) <= cfg_cnt)) begin
c_state_nx[C_WAIT] = 1'b1;
end
else c_state_nx[C_CONFIG] = 1'b1;
end
c_state[C_WAIT] : begin
if (cfg_addr_ready & cfg_data_ready) begin
c_state_nx[C_ENABLE] = 1'b1;
end
else c_state_nx[C_WAIT] = 1'b1;
end
c_state[C_ENABLE] : begin
c_state_nx[C_IDLE] = 1'b1;
end
default : begin
c_state_nx[C_IDLE] = 1'b1;
end
endcase
end
axis_addr #(
.CONFIG_DWIDTH (CONFIG_DWIDTH),
.WIDTH_RATIO (WIDTH_RATIO),
.CONVERT_SHIFT ($clog2(WIDTH_RATIO)),
.AXI_ID_WIDTH (AXI_ID_WIDTH),
.AXI_LEN_WIDTH (AXI_LEN_WIDTH),
.AXI_ADDR_WIDTH (AXI_ADDR_WIDTH))
axis_addr_ (
.clk (clk),
.rst (rst),
.cfg_address (cfg_address),
.cfg_length (cfg_length),
.cfg_valid (cfg_enable),
.cfg_ready (cfg_addr_ready),
.axi_aready (axi_awready),
.axi_aid (axi_awid),
.axi_aaddr (axi_awaddr),
.axi_alen (axi_awlen),
.axi_avalid (axi_awvalid)
);
axis_write_data #(
.BUF_AWIDTH (BUF_AWIDTH),
.CONFIG_DWIDTH (CONFIG_DWIDTH),
.WIDTH_RATIO (WIDTH_RATIO),
.CONVERT_SHIFT ($clog2(WIDTH_RATIO)),
.AXI_LEN_WIDTH (AXI_LEN_WIDTH),
.AXI_DATA_WIDTH (AXI_DATA_WIDTH),
.DATA_WIDTH (DATA_WIDTH))
axis_write_data_ (
.clk (clk),
.rst (rst),
.cfg_length (cfg_length),
.cfg_valid (cfg_enable),
.cfg_ready (cfg_data_ready),
.axi_wlast (axi_wlast),
.axi_wdata (axi_wdata),
.axi_wvalid (axi_wvalid),
.axi_wready (axi_wready),
.data (data),
.valid (valid),
.ready (ready)
);
endmodule
`endif // `ifndef _axis_write_

210
hdl/axis/axis_write_data.v Normal file
View File

@ -0,0 +1,210 @@
/**
* Module:
* axis_write_data
*
* Description:
* The axis_write_data handles the AXI write data channel.
*
* Test bench:
* axis_write_tb.v
*
* Created:
* Tue Nov 4 22:18:14 EST 2014
*
* Author:
* Berin Martini (berin.martini@gmail.com)
*/
`ifndef _axis_write_data_ `define _axis_write_data_
`include "fifo_simple.v"
`include "axis_deserializer.v"
module axis_write_data
#(parameter
BUF_AWIDTH = 9,
CONFIG_DWIDTH = 32,
WIDTH_RATIO = 16,
CONVERT_SHIFT = 3,
AXI_LEN_WIDTH = 8,
AXI_DATA_WIDTH = 32,
DATA_WIDTH = 32)
(input clk,
input rst,
input [CONFIG_DWIDTH-1:0] cfg_length,
input cfg_valid,
output cfg_ready,
output axi_wlast,
output [AXI_DATA_WIDTH-1:0] axi_wdata,
output axi_wvalid,
input axi_wready,
input [DATA_WIDTH-1:0] data,
input valid,
output reg ready
);
/**
* Local parameters
*/
localparam BURST_WIDTH = AXI_LEN_WIDTH+CONVERT_SHIFT;
localparam BURST_LAST = (1<<BURST_WIDTH)-1;
localparam
IDLE = 0,
ACTIVE = 1,
WAIT = 2,
DONE = 3;
`ifdef VERBOSE
initial $display("\using 'axis_write_data'\n");
`endif
/**
* Internal signals
*/
reg [3:0] state;
reg [3:0] state_nx;
reg [CONFIG_DWIDTH-1:0] str_cnt;
reg [CONFIG_DWIDTH-1:0] str_length;
wire [BUF_AWIDTH:0] buf_count;
wire buf_pop;
wire buf_empty;
reg deser_last;
wire [DATA_WIDTH-1:0] deser_data;
reg deser_valid;
/**
* Implementation
*/
assign cfg_ready = state[IDLE];
assign buf_pop = ~buf_empty & axi_wready;
always @(posedge clk)
if (cfg_valid) begin
str_length <= cfg_length-1;
end
// use axi_wready as a stall signal
always @(posedge clk)
if (state[IDLE]) deser_valid <= 1'b0;
else if (axi_wready) deser_valid <= buf_pop;
// half way mark ready flag
always @(posedge clk)
if (state[IDLE]) ready <= 1'b0;
else ready <= ~|(buf_count[BUF_AWIDTH:BUF_AWIDTH-1]);
always @(posedge clk)
if (state[IDLE]) str_cnt <= 'b0;
else if (axi_wready) str_cnt <= str_cnt + buf_pop;
always @(posedge clk)
if (state[IDLE]) deser_last <= 1'b0;
else if (axi_wready) begin
// trigger on last word in stream or last word in burst
deser_last <= buf_pop &
((str_length == str_cnt) | (BURST_LAST == str_cnt[0 +: BURST_WIDTH]));
end
fifo_simple #(
.DATA_WIDTH (DATA_WIDTH),
.ADDR_WIDTH (BUF_AWIDTH))
buffer_ (
.clk (clk),
.rst (state[IDLE]),
.count (buf_count),
.empty (buf_empty),
.empty_a (),
.full (),
.full_a (),
.push_data (data),
.push (valid),
.pop_data (deser_data),
.pop (buf_pop)
);
axis_deserializer #(
.DATA_NB (WIDTH_RATIO),
.DATA_WIDTH (DATA_WIDTH))
deser_ (
.clk (clk),
.rst (state[IDLE]),
.up_data (deser_data),
.up_valid (deser_valid),
.up_ready (),
.up_last (deser_last),
.down_data (axi_wdata),
.down_valid (axi_wvalid),
.down_ready (axi_wready),
.down_last (axi_wlast)
);
always @(posedge clk)
if (rst) begin
state <= 'b0;
state[IDLE] <= 1'b1;
end
else state <= state_nx;
always @* begin : DATA_
state_nx <= 'b0;
case (1'b1)
state[IDLE] : begin
if (cfg_valid) begin
state_nx[ACTIVE] <= 1'b1;
end
else state_nx[IDLE] <= 1'b1;
end
state[ACTIVE] : begin
if (axi_wready & buf_pop & (str_length == str_cnt)) begin
state_nx[WAIT] <= 1'b1;
end
else state_nx[ACTIVE] <= 1'b1;
end
state[WAIT] : begin
if (axi_wready & axi_wlast) begin
state_nx[DONE] <= 1'b1;
end
else state_nx[WAIT] <= 1'b1;
end
state[DONE] : begin
state_nx[IDLE] <= 1'b1;
end
default : begin
state_nx[IDLE] <= 1'b1;
end
endcase
end
endmodule
`endif // `ifndef _axis_write_data_

View File

@ -0,0 +1,375 @@
/**
* Testbench:
* axis_write_data
*
* Created:
* Tue Nov 4 22:17:15 EST 2014
*
* Author:
* Berin Martini (berin.martini@gmail.com)
*/
`timescale 1ns/10ps
`define TB_VERBOSE
//`define VERBOSE
`include "axis_write_data.v"
module axis_write_data_tb;
/**
* Clock and control functions
*/
// Generate a clk
reg clk;
always #1 clk = !clk;
// End of simulation event definition
event end_trigger;
always @(end_trigger) $finish;
`ifdef TB_VERBOSE
// Display header information
initial #1 display_header();
always @(end_trigger) display_header();
// And strobe signals at each clk
always @(posedge clk) display_signals();
`endif
// initial begin
// $dumpfile("result.vcd"); // Waveform file
// $dumpvars;
// end
/**
* Local parameters
*/
localparam STREAM_LENGTH = (256*8*2)-4;
localparam BUF_AWIDTH = 4;
localparam CONFIG_DWIDTH = 32;
localparam WIDTH_RATIO = 8;
localparam AXI_DATA_WIDTH = 256;
localparam DATA_WIDTH = 32;
`ifdef TB_VERBOSE
initial $display("Testbench for unit 'axis_write_data'");
`endif
/**
* signals, registers and wires
*/
reg rst;
wire done;
reg [CONFIG_DWIDTH-1:0] cfg_length;
reg cfg_valid;
wire axi_wlast;
wire [AXI_DATA_WIDTH-1:0] axi_wdata;
wire axi_wvalid;
reg axi_wready;
reg [DATA_WIDTH-1:0] data;
reg valid;
wire ready;
/**
* Unit under test
*/
axis_write_data #(
.BUF_AWIDTH (BUF_AWIDTH),
.CONFIG_DWIDTH (CONFIG_DWIDTH),
.WIDTH_RATIO (WIDTH_RATIO),
.AXI_DATA_WIDTH (AXI_DATA_WIDTH),
.DATA_WIDTH (DATA_WIDTH))
uut (
.clk (clk),
.rst (rst),
.done (done),
.cfg_length (cfg_length),
.cfg_valid (cfg_valid),
.axi_wlast (axi_wlast),
.axi_wdata (axi_wdata),
.axi_wvalid (axi_wvalid),
.axi_wready (axi_wready),
.data (data),
.valid (valid),
.ready (ready)
);
/**
* Wave form display
*/
task display_signals;
$display(
"%d\t%d",
$time, rst,
"\t%d\t%b",
cfg_length,
cfg_valid,
"\t%x\t%b\t%b\t%b",
axi_wdata,
axi_wvalid,
axi_wready,
axi_wlast,
"\t%x\t%b\t%b",
data,
valid,
ready,
"\t%b",
done,
"\t%b",
uut.state,
);
endtask // display_signals
task display_header;
$display(
"\t\ttime\trst",
"\t\tc_l",
"\tc_v",
"\t\t\t\t\t\tw_d",
"\t\t\t\tw_v",
"\tw_r",
"\tw_l",
"\t\ts_d",
"\ts_v",
"\ts_r",
"\tdone",
);
endtask
/**
* Testbench program
*/
initial begin
// init values
clk = 0;
rst = 0;
cfg_length = 'b0;
cfg_valid = 'b0;
axi_wready = 'b0;
data = 'b0;
valid = 'b0;
//end init
`ifdef TB_VERBOSE
$display("RESET");
`endif
repeat(6) @(negedge clk);
rst <= 1'b1;
repeat(6) @(negedge clk);
rst <= 1'b0;
@(negedge clk);
`ifdef TB_VERBOSE
$display("send config id, start address and length");
`endif
repeat(5) @(negedge clk);
cfg_length <= 8;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_length <= 'b0;
cfg_valid <= 1'b0;
repeat(5) @(negedge clk);
`ifdef TB_VERBOSE
$display("test write");
`endif
axi_wready <= 1'b1;
data <= 'b0;
valid <= 1'b0;
repeat(5) @(negedge clk);
repeat (5) begin
data <= data + 1;
valid <= 1'b1;
@(negedge clk);
end
axi_wready <= 1'b0;
//data <= 'b0;
valid <= 1'b0;
repeat(5) @(negedge clk);
repeat (3) begin
data <= data + 1;
valid <= 1'b1;
@(negedge clk);
end
axi_wready <= 1'b1;
//data <= 'b0;
valid <= 1'b0;
repeat(4) @(negedge clk);
axi_wready <= 1'b1;
@(negedge clk);
axi_wready <= 1'b0;
@(negedge clk);
axi_wready <= 1'b1;
@(negedge clk);
axi_wready <= 1'b0;
@(negedge clk);
axi_wready <= 1'b1;
@(negedge clk);
axi_wready <= 1'b0;
@(negedge clk);
axi_wready <= 1'b1;
@(negedge clk);
axi_wready <= 1'b0;
@(negedge clk);
axi_wready <= 1'b0;
@(negedge clk);
axi_wready <= 1'b1;
repeat(15) @(negedge clk);
`ifdef TB_VERBOSE
$display("RESET");
`endif
repeat(6) @(negedge clk);
rst <= 1'b1;
repeat(6) @(negedge clk);
rst <= 1'b0;
@(negedge clk);
`ifdef TB_VERBOSE
$display("send config id, start address and length");
`endif
repeat(5) @(negedge clk);
cfg_length <= 8;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_length <= 'b0;
cfg_valid <= 1'b0;
repeat(5) @(negedge clk);
`ifdef TB_VERBOSE
$display("test write address channel");
`endif
repeat(5) @(negedge clk);
axi_wready <= 1'b1;
//data <= 'b0;
valid <= 1'b0;
repeat(5) @(negedge clk);
repeat (8) begin
data <= data + 1;
valid <= 1'b1;
@(negedge clk);
valid <= 1'b0;
repeat(5) @(negedge clk);
end
valid <= 1'b0;
repeat(15) @(negedge clk);
axi_wready <= 1'b0;
repeat(15) @(negedge clk);
`ifdef TB_VERBOSE
$display("RESET");
`endif
repeat(6) @(negedge clk);
rst <= 1'b1;
repeat(6) @(negedge clk);
rst <= 1'b0;
@(negedge clk);
`ifdef TB_VERBOSE
$display("send config id, start address and length");
`endif
repeat(5) @(negedge clk);
cfg_length <= STREAM_LENGTH;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_length <= 'b0;
cfg_valid <= 1'b0;
repeat(5) @(negedge clk);
`ifdef TB_VERBOSE
$display("test write address channel");
`endif
repeat(5) @(negedge clk);
data <= 'b0;
valid <= 1'b0;
axi_wready <= 1'b1;
repeat(15) @(negedge clk);
repeat (STREAM_LENGTH) begin
data <= data + 1;
valid <= 1'b1;
@(negedge clk);
end
valid <= 1'b0;
repeat(15) @(negedge clk);
axi_wready <= 1'b0;
repeat(15) @(negedge clk);
`ifdef TB_VERBOSE
$display("END");
`endif
-> end_trigger;
end
endmodule

443
hdl/axis/axis_write_tb.v Normal file
View File

@ -0,0 +1,443 @@
/**
* Testbench:
* axis_write
*
* Created:
* Tue Nov 4 22:17:15 EST 2014
*
* Author:
* Berin Martini (berin.martini@gmail.com)
*/
`timescale 1ns/10ps
`define TB_VERBOSE
//`define VERBOSE
`include "axis_write.v"
module axis_write_tb;
/**
* Clock and control functions
*/
// Generate a clk
reg clk;
always #1 clk = !clk;
// End of simulation event definition
event end_trigger;
always @(end_trigger) $finish;
`ifdef TB_VERBOSE
// Display header information
initial #1 display_header();
always @(end_trigger) display_header();
// And strobe signals at each clk
always @(posedge clk) display_signals();
`endif
// initial begin
// $dumpfile("result.vcd"); // Waveform file
// $dumpvars;
// end
/**
* Local parameters
*/
localparam STREAM_LENGTH = (256*8*2)-4;
localparam BUF_AWIDTH = 4;
localparam CONFIG_ID = 1;
localparam CONFIG_ADDR = 23;
localparam CONFIG_DATA = 24;
localparam CONFIG_AWIDTH = 5;
localparam CONFIG_DWIDTH = 32;
localparam AXI_ID_WIDTH = 8;
localparam AXI_LEN_WIDTH = 2;
localparam AXI_ADDR_WIDTH = 32;
localparam AXI_DATA_WIDTH = 256;
localparam DATA_WIDTH = 32;
`ifdef TB_VERBOSE
initial $display("Testbench for unit 'axis_write'");
`endif
/**
* signals, registers and wires
*/
reg rst;
reg [CONFIG_AWIDTH-1:0] cfg_addr;
reg [CONFIG_DWIDTH-1:0] cfg_data;
reg cfg_valid;
reg axi_awready;
wire [AXI_ID_WIDTH-1:0] axi_awid;
wire [AXI_ADDR_WIDTH-1:0] axi_awaddr;
//wire [7:0] axi_awlen;
wire [AXI_LEN_WIDTH-1:0] axi_awlen;
wire axi_awvalid;
wire axi_wlast;
wire [AXI_DATA_WIDTH-1:0] axi_wdata;
wire axi_wvalid;
reg axi_wready;
reg [DATA_WIDTH-1:0] data;
reg valid;
wire ready;
/**
* Unit under test
*/
axis_write #(
.BUF_AWIDTH (BUF_AWIDTH),
.CONFIG_ID (CONFIG_ID),
.CONFIG_ADDR (CONFIG_ADDR),
.CONFIG_DATA (CONFIG_DATA),
.CONFIG_AWIDTH (CONFIG_AWIDTH),
.CONFIG_DWIDTH (CONFIG_DWIDTH),
.AXI_ID_WIDTH (AXI_ID_WIDTH),
.AXI_LEN_WIDTH (AXI_LEN_WIDTH),
.AXI_ADDR_WIDTH (AXI_ADDR_WIDTH),
.AXI_DATA_WIDTH (AXI_DATA_WIDTH),
.DATA_WIDTH (DATA_WIDTH))
uut (
.clk (clk),
.rst (rst),
.cfg_addr (cfg_addr),
.cfg_data (cfg_data),
.cfg_valid (cfg_valid),
.axi_awready (axi_awready),
.axi_awid (axi_awid),
.axi_awaddr (axi_awaddr),
.axi_awlen (axi_awlen[0 +: AXI_LEN_WIDTH]),
.axi_awvalid (axi_awvalid),
.axi_wlast (axi_wlast),
.axi_wdata (axi_wdata),
.axi_wvalid (axi_wvalid),
.axi_wready (axi_wready),
.data (data),
.valid (valid),
.ready (ready)
);
/**
* Wave form display
*/
task display_signals;
$display(
"%d\t%d",
$time, rst,
"\t%d\t%d\t%b",
cfg_addr,
cfg_data,
cfg_valid,
"\t%d\t%d\t%d\t%b\t%b",
axi_awid,
axi_awaddr,
axi_awlen,
axi_awvalid,
axi_awready,
"\t%x\t%b\t%b\t%b",
axi_wdata,
axi_wvalid,
axi_wready,
axi_wlast,
"\t%d\t%b\t%b",
data,
valid,
ready,
"\t%b",
uut.c_state,
"\t%b",
uut.axis_addr_.state,
);
endtask // display_signals
task display_header;
$display(
"\t\ttime\trst",
"\tc_a",
"\t\tc_d",
"\tc_v",
"\taw_id",
"\t\taw_a",
"\taw_l",
"\taw_v",
"\taw_r",
"\t\t\t\t\t\tw_d",
"\t\t\t\tw_v",
"\tw_r",
"\tw_l",
"\t\ts_d",
"\ts_v",
"\tm_r",
);
endtask
/**
* Testbench program
*/
initial begin
// init values
clk = 0;
rst = 0;
cfg_addr = 'b0;
cfg_data = 'b0;
cfg_valid = 'b0;
axi_awready = 'b0;
axi_wready = 'b0;
data = 'b0;
valid = 'b0;
//end init
`ifdef TB_VERBOSE
$display("RESET");
`endif
repeat(6) @(negedge clk);
rst <= 1'b1;
repeat(6) @(negedge clk);
rst <= 1'b0;
@(negedge clk);
`ifdef TB_VERBOSE
$display("send config id, start address and length");
`endif
repeat(5) @(negedge clk);
cfg_addr <= CONFIG_ADDR;
cfg_data <= CONFIG_ID;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_addr <= 'b0;
cfg_data <= 'b0;
cfg_valid <= 1'b0;
repeat(5) @(negedge clk);
cfg_addr <= CONFIG_DATA;
cfg_data <= 4;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_addr <= 'b0;
cfg_data <= 'b0;
cfg_valid <= 1'b0;
repeat(5) @(negedge clk);
cfg_addr <= CONFIG_DATA;
cfg_data <= 8;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_addr <= 'b0;
cfg_data <= 'b0;
cfg_valid <= 1'b0;
repeat(5) @(negedge clk);
`ifdef TB_VERBOSE
$display("test write address channel");
`endif
repeat(3) @(negedge clk);
axi_awready <= 1'b1;
repeat(5) @(negedge clk);
axi_wready <= 1'b1;
data <= 'b0;
valid <= 1'b0;
repeat(5) @(negedge clk);
repeat (5) begin
data <= data + 1;
valid <= 1'b1;
@(negedge clk);
end
axi_wready <= 1'b0;
//data <= 'b0;
valid <= 1'b0;
repeat(5) @(negedge clk);
repeat (3) begin
data <= data + 1;
valid <= 1'b1;
@(negedge clk);
end
axi_wready <= 1'b1;
data <= 'b0;
valid <= 1'b0;
//repeat(4) @(negedge clk);
repeat(5) @(negedge clk);
//repeat(6) @(negedge clk);
axi_wready <= 1'b0;
@(negedge clk);
axi_wready <= 1'b1;
repeat(15) @(negedge clk);
`ifdef TB_VERBOSE
$display("send config id, start address and length");
`endif
repeat(5) @(negedge clk);
cfg_addr <= CONFIG_ADDR;
cfg_data <= CONFIG_ID;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_addr <= CONFIG_DATA;
cfg_data <= 4;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_addr <= CONFIG_DATA;
cfg_data <= 8;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_addr <= 'b0;
cfg_data <= 'b0;
cfg_valid <= 1'b0;
repeat(5) @(negedge clk);
`ifdef TB_VERBOSE
$display("test write address channel");
`endif
repeat(3) @(negedge clk);
axi_awready <= 1'b1;
repeat(5) @(negedge clk);
axi_wready <= 1'b1;
data <= 'b0;
valid <= 1'b0;
repeat(5) @(negedge clk);
repeat (8) begin
data <= data + 1;
valid <= 1'b1;
@(negedge clk);
valid <= 1'b0;
repeat(5) @(negedge clk);
end
axi_awready <= 1'b0;
axi_wready <= 1'b0;
repeat(15) @(negedge clk);
`ifdef TB_VERBOSE
$display("send config id, start address and length");
`endif
repeat(5) @(negedge clk);
cfg_addr <= CONFIG_ADDR;
cfg_data <= CONFIG_ID;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_addr <= CONFIG_DATA;
cfg_data <= 255;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_addr <= CONFIG_DATA;
cfg_data <= STREAM_LENGTH;
cfg_valid <= 1'b1;
@(negedge clk)
cfg_addr <= 'b0;
cfg_data <= 'b0;
cfg_valid <= 1'b0;
repeat(5) @(negedge clk);
`ifdef TB_VERBOSE
$display("test long write");
`endif
repeat(3) @(negedge clk);
// ready to recive address
axi_awready <= 1'b1;
repeat(5) @(negedge clk);
// ready to recive data
axi_wready <= 1'b1;
data <= 'b0;
valid <= 1'b0;
repeat(5) @(negedge clk);
repeat (STREAM_LENGTH) begin
// stream data into axis
data <= data + 1;
valid <= 1'b1;
@(negedge clk);
end
valid <= 1'b0;
repeat(15) @(negedge clk);
axi_wready <= 1'b0;
repeat(15) @(negedge clk);
`ifdef TB_VERBOSE
$display("END");
`endif
-> end_trigger;
end
endmodule