mirror of
https://github.com/corundum/corundum.git
synced 2025-01-16 08:12:53 +08:00
Add COBS encoder and decoder modules and testbench
This commit is contained in:
parent
e6d78b7ca7
commit
24f7aee8b2
328
rtl/axis_cobs_decode.v
Normal file
328
rtl/axis_cobs_decode.v
Normal file
@ -0,0 +1,328 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* AXI4-Stream consistent overhead byte stuffing (COBS) decoder
|
||||
*/
|
||||
module axis_cobs_decode
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* AXI input
|
||||
*/
|
||||
input wire [7:0] input_axis_tdata,
|
||||
input wire input_axis_tvalid,
|
||||
output wire input_axis_tready,
|
||||
input wire input_axis_tlast,
|
||||
input wire input_axis_tuser,
|
||||
|
||||
/*
|
||||
* AXI output
|
||||
*/
|
||||
output wire [7:0] output_axis_tdata,
|
||||
output wire output_axis_tvalid,
|
||||
input wire output_axis_tready,
|
||||
output wire output_axis_tlast,
|
||||
output wire output_axis_tuser
|
||||
);
|
||||
|
||||
// state register
|
||||
localparam [1:0]
|
||||
STATE_IDLE = 2'd0,
|
||||
STATE_SEGMENT = 2'd1,
|
||||
STATE_NEXT_SEGMENT = 2'd2;
|
||||
|
||||
reg [1:0] state_reg = STATE_IDLE, state_next;
|
||||
|
||||
reg [7:0] count_reg = 8'd0, count_next;
|
||||
reg suppress_zero_reg = 1'b0, suppress_zero_next;
|
||||
|
||||
reg [7:0] temp_tdata_reg = 8'd0, temp_tdata_next;
|
||||
reg temp_tvalid_reg = 1'b0, temp_tvalid_next;
|
||||
|
||||
// internal datapath
|
||||
reg [7:0] output_axis_tdata_int;
|
||||
reg output_axis_tvalid_int;
|
||||
reg output_axis_tready_int_reg = 1'b0;
|
||||
reg output_axis_tlast_int;
|
||||
reg output_axis_tuser_int;
|
||||
wire output_axis_tready_int_early;
|
||||
|
||||
reg input_axis_tready_reg = 1'b0, input_axis_tready_next;
|
||||
|
||||
assign input_axis_tready = input_axis_tready_reg;
|
||||
|
||||
always @* begin
|
||||
state_next = STATE_IDLE;
|
||||
|
||||
count_next = count_reg;
|
||||
suppress_zero_next = suppress_zero_reg;
|
||||
|
||||
temp_tdata_next = temp_tdata_reg;
|
||||
temp_tvalid_next = temp_tvalid_reg;
|
||||
|
||||
output_axis_tdata_int = 8'd0;
|
||||
output_axis_tvalid_int = 1'b0;
|
||||
output_axis_tlast_int = 1'b0;
|
||||
output_axis_tuser_int = 1'b0;
|
||||
|
||||
input_axis_tready_next = 1'b0;
|
||||
|
||||
case (state_reg)
|
||||
STATE_IDLE: begin
|
||||
// idle state
|
||||
input_axis_tready_next = output_axis_tready_int_early | ~temp_tvalid_reg;
|
||||
|
||||
// output final word
|
||||
output_axis_tdata_int = temp_tdata_reg;
|
||||
output_axis_tvalid_int = temp_tvalid_reg;
|
||||
output_axis_tlast_int = temp_tvalid_reg;
|
||||
temp_tvalid_next = temp_tvalid_reg & ~output_axis_tready_int_reg;
|
||||
|
||||
if (input_axis_tready & input_axis_tvalid) begin
|
||||
// valid input data
|
||||
// skip any leading zeros
|
||||
if (input_axis_tdata != 8'd0) begin
|
||||
// store count value and zero suppress
|
||||
count_next = input_axis_tdata-1;
|
||||
suppress_zero_next = (input_axis_tdata == 8'd255);
|
||||
input_axis_tready_next = output_axis_tready_int_early;
|
||||
if (input_axis_tdata == 8'd1) begin
|
||||
// next byte will be count value
|
||||
state_next = STATE_NEXT_SEGMENT;
|
||||
end else begin
|
||||
// next byte will be data
|
||||
state_next = STATE_SEGMENT;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end
|
||||
STATE_SEGMENT: begin
|
||||
// receive segment
|
||||
input_axis_tready_next = output_axis_tready_int_early;
|
||||
|
||||
if (input_axis_tready & input_axis_tvalid) begin
|
||||
// valid input data
|
||||
// store in temp register
|
||||
temp_tdata_next = input_axis_tdata;
|
||||
temp_tvalid_next = 1'b1;
|
||||
// move temp to output
|
||||
output_axis_tdata_int = temp_tdata_reg;
|
||||
output_axis_tvalid_int = temp_tvalid_reg;
|
||||
// decrement count
|
||||
count_next = count_reg - 1;
|
||||
if (input_axis_tdata == 8'd0) begin
|
||||
// got a zero byte in a frame - mark it as an error and re-sync
|
||||
temp_tvalid_next = 1'b0;
|
||||
output_axis_tvalid_int = 1'b1;
|
||||
output_axis_tuser_int = 1'b1;
|
||||
output_axis_tlast_int = 1'b1;
|
||||
input_axis_tready_next = 1'b1;
|
||||
state_next = STATE_IDLE;
|
||||
end else if (input_axis_tlast) begin
|
||||
// end of frame
|
||||
if (count_reg == 8'd1 && ~input_axis_tuser) begin
|
||||
// end of frame indication at correct time, go to idle to output final byte
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
// end of frame indication at invalid time or tuser assert, so mark as an error and re-sync
|
||||
temp_tvalid_next = 1'b0;
|
||||
output_axis_tvalid_int = 1'b1;
|
||||
output_axis_tuser_int = 1'b1;
|
||||
output_axis_tlast_int = 1'b1;
|
||||
input_axis_tready_next = 1'b1;
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end else if (count_reg == 8'd1) begin
|
||||
// next byte will be count value
|
||||
state_next = STATE_NEXT_SEGMENT;
|
||||
end else begin
|
||||
// next byte will be data
|
||||
state_next = STATE_SEGMENT;
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_SEGMENT;
|
||||
end
|
||||
end
|
||||
STATE_NEXT_SEGMENT: begin
|
||||
// next segment
|
||||
input_axis_tready_next = output_axis_tready_int_early;
|
||||
|
||||
if (input_axis_tready & input_axis_tvalid) begin
|
||||
// valid input data
|
||||
// store zero in temp if not suppressed
|
||||
temp_tdata_next = 8'd0;
|
||||
temp_tvalid_next = ~suppress_zero_reg;
|
||||
// move temp to output
|
||||
output_axis_tdata_int = temp_tdata_reg;
|
||||
output_axis_tvalid_int = temp_tvalid_reg;
|
||||
if (input_axis_tdata == 8'd0) begin
|
||||
// got a zero byte delineating the end of the frame, so mark as such and re-sync
|
||||
temp_tvalid_next = 1'b0;
|
||||
output_axis_tuser_int = input_axis_tuser;
|
||||
output_axis_tlast_int = 1'b1;
|
||||
input_axis_tready_next = 1'b1;
|
||||
state_next = STATE_IDLE;
|
||||
end else if (input_axis_tlast) begin
|
||||
if (input_axis_tdata == 8'd1 && ~input_axis_tuser) begin
|
||||
// end of frame indication at correct time, go to idle to output final byte
|
||||
state_next = STATE_IDLE;
|
||||
end else begin
|
||||
// end of frame indication at invalid time or tuser assert, so mark as an error and re-sync
|
||||
temp_tvalid_next = 1'b0;
|
||||
output_axis_tvalid_int = 1'b1;
|
||||
output_axis_tuser_int = 1'b1;
|
||||
output_axis_tlast_int = 1'b1;
|
||||
input_axis_tready_next = 1'b1;
|
||||
state_next = STATE_IDLE;
|
||||
end
|
||||
end else begin
|
||||
// otherwise, store count value and zero suppress
|
||||
count_next = input_axis_tdata-1;
|
||||
suppress_zero_next = (input_axis_tdata == 8'd255);
|
||||
input_axis_tready_next = output_axis_tready_int_early;
|
||||
if (input_axis_tdata == 8'd1) begin
|
||||
// next byte will be count value
|
||||
state_next = STATE_NEXT_SEGMENT;
|
||||
end else begin
|
||||
// next byte will be data
|
||||
state_next = STATE_SEGMENT;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
state_next = STATE_NEXT_SEGMENT;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
state_reg <= STATE_IDLE;
|
||||
temp_tvalid_reg <= 1'b0;
|
||||
input_axis_tready_reg <= 1'b0;
|
||||
end else begin
|
||||
state_reg <= state_next;
|
||||
temp_tvalid_reg <= temp_tvalid_next;
|
||||
input_axis_tready_reg <= input_axis_tready_next;
|
||||
end
|
||||
|
||||
temp_tdata_reg <= temp_tdata_next;
|
||||
|
||||
count_reg <= count_next;
|
||||
suppress_zero_reg <= suppress_zero_next;
|
||||
end
|
||||
|
||||
// output datapath logic
|
||||
reg [7:0] output_axis_tdata_reg = 8'd0;
|
||||
reg output_axis_tvalid_reg = 1'b0, output_axis_tvalid_next;
|
||||
reg output_axis_tlast_reg = 1'b0;
|
||||
reg output_axis_tuser_reg = 1'b0;
|
||||
|
||||
reg [7:0] temp_axis_tdata_reg = 8'd0;
|
||||
reg temp_axis_tvalid_reg = 1'b0, temp_axis_tvalid_next;
|
||||
reg temp_axis_tlast_reg = 1'b0;
|
||||
reg temp_axis_tuser_reg = 1'b0;
|
||||
|
||||
// datapath control
|
||||
reg store_axis_int_to_output;
|
||||
reg store_axis_int_to_temp;
|
||||
reg store_axis_temp_to_output;
|
||||
|
||||
assign output_axis_tdata = output_axis_tdata_reg;
|
||||
assign output_axis_tvalid = output_axis_tvalid_reg;
|
||||
assign output_axis_tlast = output_axis_tlast_reg;
|
||||
assign output_axis_tuser = output_axis_tuser_reg;
|
||||
|
||||
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
|
||||
assign output_axis_tready_int_early = output_axis_tready | (~temp_axis_tvalid_reg & (~output_axis_tvalid_reg | ~output_axis_tvalid_int));
|
||||
|
||||
always @* begin
|
||||
// transfer sink ready state to source
|
||||
output_axis_tvalid_next = output_axis_tvalid_reg;
|
||||
temp_axis_tvalid_next = temp_axis_tvalid_reg;
|
||||
|
||||
store_axis_int_to_output = 1'b0;
|
||||
store_axis_int_to_temp = 1'b0;
|
||||
store_axis_temp_to_output = 1'b0;
|
||||
|
||||
if (output_axis_tready_int_reg) begin
|
||||
// input is ready
|
||||
if (output_axis_tready | ~output_axis_tvalid_reg) begin
|
||||
// output is ready or currently not valid, transfer data to output
|
||||
output_axis_tvalid_next = output_axis_tvalid_int;
|
||||
store_axis_int_to_output = 1'b1;
|
||||
end else begin
|
||||
// output is not ready, store input in temp
|
||||
temp_axis_tvalid_next = output_axis_tvalid_int;
|
||||
store_axis_int_to_temp = 1'b1;
|
||||
end
|
||||
end else if (output_axis_tready) begin
|
||||
// input is not ready, but output is ready
|
||||
output_axis_tvalid_next = temp_axis_tvalid_reg;
|
||||
temp_axis_tvalid_next = 1'b0;
|
||||
store_axis_temp_to_output = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
output_axis_tvalid_reg <= 1'b0;
|
||||
output_axis_tready_int_reg <= 1'b0;
|
||||
temp_axis_tvalid_reg <= 1'b0;
|
||||
end else begin
|
||||
output_axis_tvalid_reg <= output_axis_tvalid_next;
|
||||
output_axis_tready_int_reg <= output_axis_tready_int_early;
|
||||
temp_axis_tvalid_reg <= temp_axis_tvalid_next;
|
||||
end
|
||||
|
||||
// datapath
|
||||
if (store_axis_int_to_output) begin
|
||||
output_axis_tdata_reg <= output_axis_tdata_int;
|
||||
output_axis_tlast_reg <= output_axis_tlast_int;
|
||||
output_axis_tuser_reg <= output_axis_tuser_int;
|
||||
end else if (store_axis_temp_to_output) begin
|
||||
output_axis_tdata_reg <= temp_axis_tdata_reg;
|
||||
output_axis_tlast_reg <= temp_axis_tlast_reg;
|
||||
output_axis_tuser_reg <= temp_axis_tuser_reg;
|
||||
end
|
||||
|
||||
if (store_axis_int_to_temp) begin
|
||||
temp_axis_tdata_reg <= output_axis_tdata_int;
|
||||
temp_axis_tlast_reg <= output_axis_tlast_int;
|
||||
temp_axis_tuser_reg <= output_axis_tuser_int;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
473
rtl/axis_cobs_encode.v
Normal file
473
rtl/axis_cobs_encode.v
Normal file
@ -0,0 +1,473 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* AXI4-Stream consistent overhead byte stuffing (COBS) encoder
|
||||
*/
|
||||
module axis_cobs_encode #
|
||||
(
|
||||
// append zero for in band framing
|
||||
parameter APPEND_ZERO = 1
|
||||
)
|
||||
(
|
||||
input wire clk,
|
||||
input wire rst,
|
||||
|
||||
/*
|
||||
* AXI input
|
||||
*/
|
||||
input wire [7:0] input_axis_tdata,
|
||||
input wire input_axis_tvalid,
|
||||
output wire input_axis_tready,
|
||||
input wire input_axis_tlast,
|
||||
input wire input_axis_tuser,
|
||||
|
||||
/*
|
||||
* AXI output
|
||||
*/
|
||||
output wire [7:0] output_axis_tdata,
|
||||
output wire output_axis_tvalid,
|
||||
input wire output_axis_tready,
|
||||
output wire output_axis_tlast,
|
||||
output wire output_axis_tuser
|
||||
);
|
||||
|
||||
// state register
|
||||
localparam [1:0]
|
||||
INPUT_STATE_IDLE = 2'd0,
|
||||
INPUT_STATE_SEGMENT = 2'd1,
|
||||
INPUT_STATE_FINAL_ZERO = 2'd2,
|
||||
INPUT_STATE_APPEND_ZERO = 2'd3;
|
||||
|
||||
reg [1:0] input_state_reg = INPUT_STATE_IDLE, input_state_next;
|
||||
|
||||
localparam [0:0]
|
||||
OUTPUT_STATE_IDLE = 1'd0,
|
||||
OUTPUT_STATE_SEGMENT = 1'd1;
|
||||
|
||||
reg [0:0] output_state_reg = OUTPUT_STATE_IDLE, output_state_next;
|
||||
|
||||
reg [7:0] input_count_reg = 8'd0, input_count_next;
|
||||
reg [7:0] output_count_reg = 8'd0, output_count_next;
|
||||
reg fail_frame_reg = 1'b0, fail_frame_next;
|
||||
|
||||
// internal datapath
|
||||
reg [7:0] output_axis_tdata_int;
|
||||
reg output_axis_tvalid_int;
|
||||
reg output_axis_tready_int_reg = 1'b0;
|
||||
reg output_axis_tlast_int;
|
||||
reg output_axis_tuser_int;
|
||||
wire output_axis_tready_int_early;
|
||||
|
||||
reg input_axis_tready_mask;
|
||||
|
||||
assign input_axis_tready = code_fifo_in_tready & data_fifo_in_tready & input_axis_tready_mask;
|
||||
|
||||
reg [7:0] code_fifo_in_tdata;
|
||||
reg code_fifo_in_tvalid;
|
||||
reg code_fifo_in_tlast;
|
||||
reg code_fifo_in_tuser;
|
||||
wire code_fifo_in_tready;
|
||||
|
||||
wire [7:0] code_fifo_out_tdata;
|
||||
wire code_fifo_out_tvalid;
|
||||
wire code_fifo_out_tlast;
|
||||
wire code_fifo_out_tuser;
|
||||
reg code_fifo_out_tready;
|
||||
|
||||
axis_fifo #(
|
||||
.ADDR_WIDTH(8),
|
||||
.DATA_WIDTH(8)
|
||||
)
|
||||
code_fifo_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
// AXI input
|
||||
.input_axis_tdata(code_fifo_in_tdata),
|
||||
.input_axis_tvalid(code_fifo_in_tvalid),
|
||||
.input_axis_tready(code_fifo_in_tready),
|
||||
.input_axis_tlast(code_fifo_in_tlast),
|
||||
.input_axis_tuser(code_fifo_in_tuser),
|
||||
// AXI output
|
||||
.output_axis_tdata(code_fifo_out_tdata),
|
||||
.output_axis_tvalid(code_fifo_out_tvalid),
|
||||
.output_axis_tready(code_fifo_out_tready),
|
||||
.output_axis_tlast(code_fifo_out_tlast),
|
||||
.output_axis_tuser(code_fifo_out_tuser)
|
||||
);
|
||||
|
||||
reg [7:0] data_fifo_in_tdata;
|
||||
reg data_fifo_in_tvalid;
|
||||
reg data_fifo_in_tlast;
|
||||
wire data_fifo_in_tready;
|
||||
|
||||
wire [7:0] data_fifo_out_tdata;
|
||||
wire data_fifo_out_tvalid;
|
||||
wire data_fifo_out_tlast;
|
||||
reg data_fifo_out_tready;
|
||||
|
||||
axis_fifo #(
|
||||
.ADDR_WIDTH(8),
|
||||
.DATA_WIDTH(8)
|
||||
)
|
||||
data_fifo_inst (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
// AXI input
|
||||
.input_axis_tdata(data_fifo_in_tdata),
|
||||
.input_axis_tvalid(data_fifo_in_tvalid),
|
||||
.input_axis_tready(data_fifo_in_tready),
|
||||
.input_axis_tlast(data_fifo_in_tlast),
|
||||
.input_axis_tuser(1'b0),
|
||||
// AXI output
|
||||
.output_axis_tdata(data_fifo_out_tdata),
|
||||
.output_axis_tvalid(data_fifo_out_tvalid),
|
||||
.output_axis_tready(data_fifo_out_tready),
|
||||
.output_axis_tlast(data_fifo_out_tlast),
|
||||
.output_axis_tuser()
|
||||
);
|
||||
|
||||
always @* begin
|
||||
input_state_next = INPUT_STATE_IDLE;
|
||||
|
||||
input_count_next = input_count_reg;
|
||||
|
||||
fail_frame_next = fail_frame_reg;
|
||||
|
||||
input_axis_tready_mask = 1'b0;
|
||||
|
||||
code_fifo_in_tdata = 8'd0;
|
||||
code_fifo_in_tvalid = 1'b0;
|
||||
code_fifo_in_tlast = 1'b0;
|
||||
code_fifo_in_tuser = 1'b0;
|
||||
|
||||
data_fifo_in_tdata = input_axis_tdata;
|
||||
data_fifo_in_tvalid = 1'b0;
|
||||
data_fifo_in_tlast = 1'b0;
|
||||
|
||||
case (input_state_reg)
|
||||
INPUT_STATE_IDLE: begin
|
||||
// idle state
|
||||
input_axis_tready_mask = 1'b1;
|
||||
fail_frame_next = 1'b0;
|
||||
|
||||
if (input_axis_tready & input_axis_tvalid) begin
|
||||
// valid input data
|
||||
|
||||
if (input_axis_tdata == 8'd0 || (input_axis_tlast & input_axis_tuser)) begin
|
||||
// got a zero or propagated error, so store a zero code
|
||||
code_fifo_in_tdata = 8'd1;
|
||||
code_fifo_in_tvalid = 1'b1;
|
||||
if (input_axis_tlast) begin
|
||||
// last byte, so close out the frame
|
||||
fail_frame_next = input_axis_tuser;
|
||||
input_state_next = INPUT_STATE_FINAL_ZERO;
|
||||
end else begin
|
||||
// return to idle to await next segment
|
||||
input_state_next = INPUT_STATE_IDLE;
|
||||
end
|
||||
end else begin
|
||||
// got something other than a zero, so store it and init the segment counter
|
||||
input_count_next = 8'd2;
|
||||
data_fifo_in_tdata = input_axis_tdata;
|
||||
data_fifo_in_tvalid = 1'b1;
|
||||
if (input_axis_tlast) begin
|
||||
// last byte, so store the code and close out the frame
|
||||
code_fifo_in_tdata = 8'd2;
|
||||
code_fifo_in_tvalid = 1'b1;
|
||||
if (APPEND_ZERO) begin
|
||||
// zero frame mode, need to add a zero code to end the frame
|
||||
input_state_next = INPUT_STATE_APPEND_ZERO;
|
||||
end else begin
|
||||
// normal frame mode, close out the frame
|
||||
data_fifo_in_tlast = 1'b1;
|
||||
input_state_next = INPUT_STATE_IDLE;
|
||||
end
|
||||
end else begin
|
||||
// await more segment data
|
||||
input_state_next = INPUT_STATE_SEGMENT;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
input_state_next = INPUT_STATE_IDLE;
|
||||
end
|
||||
end
|
||||
INPUT_STATE_SEGMENT: begin
|
||||
// encode segment
|
||||
input_axis_tready_mask = 1'b1;
|
||||
fail_frame_next = 1'b0;
|
||||
|
||||
if (input_axis_tready & input_axis_tvalid) begin
|
||||
// valid input data
|
||||
|
||||
if (input_axis_tdata == 8'd0 || (input_axis_tlast & input_axis_tuser)) begin
|
||||
// got a zero or propagated error, so store the code
|
||||
code_fifo_in_tdata = input_count_reg;
|
||||
code_fifo_in_tvalid = 1'b1;
|
||||
if (input_axis_tlast) begin
|
||||
// last byte, so close out the frame
|
||||
fail_frame_next = input_axis_tuser;
|
||||
input_state_next = INPUT_STATE_FINAL_ZERO;
|
||||
end else begin
|
||||
// return to idle to await next segment
|
||||
input_state_next = INPUT_STATE_IDLE;
|
||||
end
|
||||
end else begin
|
||||
// got something other than a zero, so store it and increment the segment counter
|
||||
input_count_next = input_count_reg+1;
|
||||
data_fifo_in_tdata = input_axis_tdata;
|
||||
data_fifo_in_tvalid = 1'b1;
|
||||
if (input_count_reg == 8'd254) begin
|
||||
// 254 bytes in frame, so dump and reset counter
|
||||
code_fifo_in_tdata = input_count_reg+1;
|
||||
code_fifo_in_tvalid = 1'b1;
|
||||
input_count_next = 8'd1;
|
||||
end
|
||||
if (input_axis_tlast) begin
|
||||
// last byte, so store the code and close out the frame
|
||||
code_fifo_in_tdata = input_count_reg+1;
|
||||
code_fifo_in_tvalid = 1'b1;
|
||||
if (APPEND_ZERO) begin
|
||||
// zero frame mode, need to add a zero code to end the frame
|
||||
input_state_next = INPUT_STATE_APPEND_ZERO;
|
||||
end else begin
|
||||
// normal frame mode, close out the frame
|
||||
data_fifo_in_tlast = 1'b1;
|
||||
input_state_next = INPUT_STATE_IDLE;
|
||||
end
|
||||
end else begin
|
||||
// await more segment data
|
||||
input_state_next = INPUT_STATE_SEGMENT;
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
input_state_next = INPUT_STATE_SEGMENT;
|
||||
end
|
||||
end
|
||||
INPUT_STATE_FINAL_ZERO: begin
|
||||
// final zero code required
|
||||
input_axis_tready_mask = 1'b0;
|
||||
|
||||
if (code_fifo_in_tready) begin
|
||||
// push a zero code and close out frame
|
||||
if (fail_frame_reg) begin
|
||||
code_fifo_in_tdata = 8'd2;
|
||||
code_fifo_in_tuser = 1'b1;
|
||||
end else begin
|
||||
code_fifo_in_tdata = 8'd1;
|
||||
end
|
||||
code_fifo_in_tvalid = 1'b1;
|
||||
if (APPEND_ZERO) begin
|
||||
// zero frame mode, need to add a zero code to end the frame
|
||||
input_state_next = INPUT_STATE_APPEND_ZERO;
|
||||
end else begin
|
||||
// normal frame mode, close out the frame
|
||||
code_fifo_in_tlast = 1'b1;
|
||||
fail_frame_next = 1'b0;
|
||||
input_state_next = INPUT_STATE_IDLE;
|
||||
end
|
||||
end else begin
|
||||
input_state_next = INPUT_STATE_FINAL_ZERO;
|
||||
end
|
||||
end
|
||||
INPUT_STATE_APPEND_ZERO: begin
|
||||
// append zero for zero framing
|
||||
input_axis_tready_mask = 1'b0;
|
||||
|
||||
if (code_fifo_in_tready) begin
|
||||
// push frame termination code and close out frame
|
||||
code_fifo_in_tdata = 8'd0;
|
||||
code_fifo_in_tlast = 1'b1;
|
||||
code_fifo_in_tuser = fail_frame_reg;
|
||||
code_fifo_in_tvalid = 1'b1;
|
||||
fail_frame_next = 1'b0;
|
||||
input_state_next = INPUT_STATE_IDLE;
|
||||
end else begin
|
||||
input_state_next = INPUT_STATE_APPEND_ZERO;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always @* begin
|
||||
output_state_next = OUTPUT_STATE_IDLE;
|
||||
|
||||
output_count_next = output_count_reg;
|
||||
|
||||
output_axis_tdata_int = 8'd0;
|
||||
output_axis_tvalid_int = 1'b0;
|
||||
output_axis_tlast_int = 1'b0;
|
||||
output_axis_tuser_int = 1'b0;
|
||||
|
||||
code_fifo_out_tready = 1'b0;
|
||||
|
||||
data_fifo_out_tready = 1'b0;
|
||||
|
||||
case (output_state_reg)
|
||||
OUTPUT_STATE_IDLE: begin
|
||||
// idle state
|
||||
|
||||
if (output_axis_tready_int_reg & code_fifo_out_tvalid) begin
|
||||
// transfer out code byte and load counter
|
||||
output_axis_tdata_int = code_fifo_out_tdata;
|
||||
output_axis_tlast_int = code_fifo_out_tlast;
|
||||
output_axis_tuser_int = code_fifo_out_tuser & code_fifo_out_tlast;
|
||||
output_count_next = code_fifo_out_tdata-1;
|
||||
output_axis_tvalid_int = 1'b1;
|
||||
code_fifo_out_tready = 1'b1;
|
||||
if (code_fifo_out_tdata == 8'd0 || code_fifo_out_tdata == 8'd1 || code_fifo_out_tuser) begin
|
||||
// frame termination and zero codes will be followed by codes
|
||||
output_state_next = OUTPUT_STATE_IDLE;
|
||||
end else begin
|
||||
// transfer out data
|
||||
output_state_next = OUTPUT_STATE_SEGMENT;
|
||||
end
|
||||
end else begin
|
||||
output_state_next = OUTPUT_STATE_IDLE;
|
||||
end
|
||||
end
|
||||
OUTPUT_STATE_SEGMENT: begin
|
||||
// segment output
|
||||
|
||||
if (output_axis_tready_int_reg & data_fifo_out_tvalid) begin
|
||||
// transfer out data byte and decrement counter
|
||||
output_axis_tdata_int = data_fifo_out_tdata;
|
||||
output_axis_tlast_int = data_fifo_out_tlast;
|
||||
output_count_next = output_count_reg - 1;
|
||||
output_axis_tvalid_int = 1'b1;
|
||||
data_fifo_out_tready = 1'b1;
|
||||
if (output_count_reg == 1'b1) begin
|
||||
// done with segment, get a code byte next
|
||||
output_state_next = OUTPUT_STATE_IDLE;
|
||||
end else begin
|
||||
// more data to transfer
|
||||
output_state_next = OUTPUT_STATE_SEGMENT;
|
||||
end
|
||||
end else begin
|
||||
output_state_next = OUTPUT_STATE_SEGMENT;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
input_state_reg <= INPUT_STATE_IDLE;
|
||||
output_state_reg <= OUTPUT_STATE_IDLE;
|
||||
end else begin
|
||||
input_state_reg <= input_state_next;
|
||||
output_state_reg <= output_state_next;
|
||||
end
|
||||
|
||||
input_count_reg <= input_count_next;
|
||||
output_count_reg <= output_count_next;
|
||||
fail_frame_reg <= fail_frame_next;
|
||||
end
|
||||
|
||||
// output datapath logic
|
||||
reg [7:0] output_axis_tdata_reg = 8'd0;
|
||||
reg output_axis_tvalid_reg = 1'b0, output_axis_tvalid_next;
|
||||
reg output_axis_tlast_reg = 1'b0;
|
||||
reg output_axis_tuser_reg = 1'b0;
|
||||
|
||||
reg [7:0] temp_axis_tdata_reg = 8'd0;
|
||||
reg temp_axis_tvalid_reg = 1'b0, temp_axis_tvalid_next;
|
||||
reg temp_axis_tlast_reg = 1'b0;
|
||||
reg temp_axis_tuser_reg = 1'b0;
|
||||
|
||||
// datapath control
|
||||
reg store_axis_int_to_output;
|
||||
reg store_axis_int_to_temp;
|
||||
reg store_axis_temp_to_output;
|
||||
|
||||
assign output_axis_tdata = output_axis_tdata_reg;
|
||||
assign output_axis_tvalid = output_axis_tvalid_reg;
|
||||
assign output_axis_tlast = output_axis_tlast_reg;
|
||||
assign output_axis_tuser = output_axis_tuser_reg;
|
||||
|
||||
// enable ready input next cycle if output is ready or the temp reg will not be filled on the next cycle (output reg empty or no input)
|
||||
assign output_axis_tready_int_early = output_axis_tready | (~temp_axis_tvalid_reg & (~output_axis_tvalid_reg | ~output_axis_tvalid_int));
|
||||
|
||||
always @* begin
|
||||
// transfer sink ready state to source
|
||||
output_axis_tvalid_next = output_axis_tvalid_reg;
|
||||
temp_axis_tvalid_next = temp_axis_tvalid_reg;
|
||||
|
||||
store_axis_int_to_output = 1'b0;
|
||||
store_axis_int_to_temp = 1'b0;
|
||||
store_axis_temp_to_output = 1'b0;
|
||||
|
||||
if (output_axis_tready_int_reg) begin
|
||||
// input is ready
|
||||
if (output_axis_tready | ~output_axis_tvalid_reg) begin
|
||||
// output is ready or currently not valid, transfer data to output
|
||||
output_axis_tvalid_next = output_axis_tvalid_int;
|
||||
store_axis_int_to_output = 1'b1;
|
||||
end else begin
|
||||
// output is not ready, store input in temp
|
||||
temp_axis_tvalid_next = output_axis_tvalid_int;
|
||||
store_axis_int_to_temp = 1'b1;
|
||||
end
|
||||
end else if (output_axis_tready) begin
|
||||
// input is not ready, but output is ready
|
||||
output_axis_tvalid_next = temp_axis_tvalid_reg;
|
||||
temp_axis_tvalid_next = 1'b0;
|
||||
store_axis_temp_to_output = 1'b1;
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk) begin
|
||||
if (rst) begin
|
||||
output_axis_tvalid_reg <= 1'b0;
|
||||
output_axis_tready_int_reg <= 1'b0;
|
||||
temp_axis_tvalid_reg <= 1'b0;
|
||||
end else begin
|
||||
output_axis_tvalid_reg <= output_axis_tvalid_next;
|
||||
output_axis_tready_int_reg <= output_axis_tready_int_early;
|
||||
temp_axis_tvalid_reg <= temp_axis_tvalid_next;
|
||||
end
|
||||
|
||||
// datapath
|
||||
if (store_axis_int_to_output) begin
|
||||
output_axis_tdata_reg <= output_axis_tdata_int;
|
||||
output_axis_tlast_reg <= output_axis_tlast_int;
|
||||
output_axis_tuser_reg <= output_axis_tuser_int;
|
||||
end else if (store_axis_temp_to_output) begin
|
||||
output_axis_tdata_reg <= temp_axis_tdata_reg;
|
||||
output_axis_tlast_reg <= temp_axis_tlast_reg;
|
||||
output_axis_tuser_reg <= temp_axis_tuser_reg;
|
||||
end
|
||||
|
||||
if (store_axis_int_to_temp) begin
|
||||
temp_axis_tdata_reg <= output_axis_tdata_int;
|
||||
temp_axis_tlast_reg <= output_axis_tlast_int;
|
||||
temp_axis_tuser_reg <= output_axis_tuser_int;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
482
tb/test_axis_cobs_decode.py
Executable file
482
tb/test_axis_cobs_decode.py
Executable file
@ -0,0 +1,482 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2016 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
from myhdl import *
|
||||
import os
|
||||
|
||||
try:
|
||||
from queue import Queue
|
||||
except ImportError:
|
||||
from Queue import Queue
|
||||
|
||||
import axis_ep
|
||||
|
||||
module = 'axis_cobs_decode'
|
||||
testbench = 'test_axis_cobs_decode'
|
||||
|
||||
srcs = []
|
||||
|
||||
srcs.append("../rtl/%s.v" % module)
|
||||
srcs.append("%s.v" % testbench)
|
||||
|
||||
src = ' '.join(srcs)
|
||||
|
||||
build_cmd = "iverilog -o %s.vvp %s" % (testbench, src)
|
||||
|
||||
def dut_axis_cobs_decode(clk,
|
||||
rst,
|
||||
current_test,
|
||||
input_axis_tdata,
|
||||
input_axis_tvalid,
|
||||
input_axis_tready,
|
||||
input_axis_tlast,
|
||||
input_axis_tuser,
|
||||
output_axis_tdata,
|
||||
output_axis_tvalid,
|
||||
output_axis_tready,
|
||||
output_axis_tlast,
|
||||
output_axis_tuser):
|
||||
|
||||
if os.system(build_cmd):
|
||||
raise Exception("Error running build command")
|
||||
return Cosimulation("vvp -m myhdl %s.vvp -lxt2" % testbench,
|
||||
clk=clk,
|
||||
rst=rst,
|
||||
current_test=current_test,
|
||||
input_axis_tdata=input_axis_tdata,
|
||||
input_axis_tvalid=input_axis_tvalid,
|
||||
input_axis_tready=input_axis_tready,
|
||||
input_axis_tlast=input_axis_tlast,
|
||||
input_axis_tuser=input_axis_tuser,
|
||||
output_axis_tdata=output_axis_tdata,
|
||||
output_axis_tvalid=output_axis_tvalid,
|
||||
output_axis_tready=output_axis_tready,
|
||||
output_axis_tlast=output_axis_tlast,
|
||||
output_axis_tuser=output_axis_tuser)
|
||||
|
||||
def cobs_encode(block):
|
||||
block = bytearray(block)
|
||||
enc = bytearray()
|
||||
|
||||
seg = bytearray()
|
||||
code = 1
|
||||
|
||||
new_data = True
|
||||
|
||||
for b in block:
|
||||
if b == 0:
|
||||
enc.append(code)
|
||||
enc.extend(seg)
|
||||
code = 1
|
||||
seg = bytearray()
|
||||
new_data = True
|
||||
else:
|
||||
code += 1
|
||||
seg.append(b)
|
||||
new_data = True
|
||||
if code == 255:
|
||||
enc.append(code)
|
||||
enc.extend(seg)
|
||||
code = 1
|
||||
seg = bytearray()
|
||||
new_data = False
|
||||
|
||||
if new_data:
|
||||
enc.append(code)
|
||||
enc.extend(seg)
|
||||
|
||||
return bytes(enc)
|
||||
|
||||
def cobs_decode(block):
|
||||
block = bytearray(block)
|
||||
dec = bytearray()
|
||||
|
||||
it = iter(bytearray(block))
|
||||
code = 0
|
||||
|
||||
i = 0
|
||||
|
||||
if 0 in block:
|
||||
return None
|
||||
|
||||
while i < len(block):
|
||||
code = block[i]
|
||||
i += 1
|
||||
if i+code-1 > len(block):
|
||||
return None
|
||||
for k in range(code-1):
|
||||
dec.append(block[i])
|
||||
i += 1
|
||||
if code < 255 and i < len(block):
|
||||
dec.append(0)
|
||||
|
||||
return bytes(dec)
|
||||
|
||||
def prbs31(state = 0x7fffffff):
|
||||
while True:
|
||||
for i in range(8):
|
||||
if bool(state & 0x08000000) ^ bool(state & 0x40000000):
|
||||
state = ((state & 0x3fffffff) << 1) | 1
|
||||
else:
|
||||
state = (state & 0x3fffffff) << 1
|
||||
yield state & 0xff
|
||||
|
||||
def bench():
|
||||
|
||||
# Parameters
|
||||
|
||||
|
||||
# Inputs
|
||||
clk = Signal(bool(0))
|
||||
rst = Signal(bool(0))
|
||||
current_test = Signal(intbv(0)[8:])
|
||||
|
||||
input_axis_tdata = Signal(intbv(0)[8:])
|
||||
input_axis_tvalid = Signal(bool(0))
|
||||
input_axis_tlast = Signal(bool(0))
|
||||
input_axis_tuser = Signal(bool(0))
|
||||
output_axis_tready = Signal(bool(0))
|
||||
|
||||
# Outputs
|
||||
input_axis_tready = Signal(bool(0))
|
||||
output_axis_tdata = Signal(intbv(0)[8:])
|
||||
output_axis_tvalid = Signal(bool(0))
|
||||
output_axis_tlast = Signal(bool(0))
|
||||
output_axis_tuser = Signal(bool(0))
|
||||
|
||||
# sources and sinks
|
||||
source_queue = Queue()
|
||||
source_pause = Signal(bool(0))
|
||||
sink_queue = Queue()
|
||||
sink_pause = Signal(bool(0))
|
||||
|
||||
source = axis_ep.AXIStreamSource(clk,
|
||||
rst,
|
||||
tdata=input_axis_tdata,
|
||||
tvalid=input_axis_tvalid,
|
||||
tready=input_axis_tready,
|
||||
tlast=input_axis_tlast,
|
||||
tuser=input_axis_tuser,
|
||||
fifo=source_queue,
|
||||
pause=source_pause,
|
||||
name='source')
|
||||
|
||||
sink = axis_ep.AXIStreamSink(clk,
|
||||
rst,
|
||||
tdata=output_axis_tdata,
|
||||
tvalid=output_axis_tvalid,
|
||||
tready=output_axis_tready,
|
||||
tlast=output_axis_tlast,
|
||||
tuser=output_axis_tuser,
|
||||
fifo=sink_queue,
|
||||
pause=sink_pause,
|
||||
name='sink')
|
||||
|
||||
# DUT
|
||||
dut = dut_axis_cobs_decode(clk,
|
||||
rst,
|
||||
current_test,
|
||||
input_axis_tdata,
|
||||
input_axis_tvalid,
|
||||
input_axis_tready,
|
||||
input_axis_tlast,
|
||||
input_axis_tuser,
|
||||
output_axis_tdata,
|
||||
output_axis_tvalid,
|
||||
output_axis_tready,
|
||||
output_axis_tlast,
|
||||
output_axis_tuser)
|
||||
|
||||
@always(delay(4))
|
||||
def clkgen():
|
||||
clk.next = not clk
|
||||
|
||||
def wait_normal():
|
||||
i = 4
|
||||
while i > 0:
|
||||
i = max(0, i-1)
|
||||
if input_axis_tvalid or output_axis_tvalid or not source_queue.empty():
|
||||
i = 4
|
||||
yield clk.posedge
|
||||
|
||||
def wait_pause_source():
|
||||
i = 2
|
||||
while i > 0:
|
||||
i = max(0, i-1)
|
||||
if input_axis_tvalid or output_axis_tvalid or not source_queue.empty():
|
||||
i = 2
|
||||
source_pause.next = True
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
source_pause.next = False
|
||||
yield clk.posedge
|
||||
|
||||
def wait_pause_sink():
|
||||
i = 2
|
||||
while i > 0:
|
||||
i = max(0, i-1)
|
||||
if input_axis_tvalid or output_axis_tvalid or not source_queue.empty():
|
||||
i = 2
|
||||
sink_pause.next = True
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
sink_pause.next = False
|
||||
yield clk.posedge
|
||||
|
||||
@instance
|
||||
def check():
|
||||
yield delay(100)
|
||||
yield clk.posedge
|
||||
rst.next = 1
|
||||
yield clk.posedge
|
||||
rst.next = 0
|
||||
yield clk.posedge
|
||||
yield delay(100)
|
||||
yield clk.posedge
|
||||
|
||||
# testbench stimulus
|
||||
|
||||
for payload_len in list(range(1,33))+list(range(252,261))+[512,1024]:
|
||||
gen = prbs31()
|
||||
for block in [bytearray([0]*payload_len),
|
||||
bytearray([k%255+1 for k in range(payload_len)]),
|
||||
b'\x00'+bytearray([k%255+1 for k in range(payload_len)])+b'\x00',
|
||||
bytearray([next(gen) for i in range(payload_len)])]:
|
||||
|
||||
yield clk.posedge
|
||||
print("test 1: test packet, length %d" % payload_len)
|
||||
current_test.next = 1
|
||||
|
||||
enc = cobs_encode(block)
|
||||
|
||||
test_frame = axis_ep.AXIStreamFrame(enc)
|
||||
|
||||
for wait in wait_normal, wait_pause_source, wait_pause_sink:
|
||||
source_queue.put(test_frame)
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
yield wait()
|
||||
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert cobs_decode(enc) == block
|
||||
assert rx_frame.data == block
|
||||
assert not rx_frame.user[-1]
|
||||
|
||||
assert sink_queue.empty()
|
||||
|
||||
yield delay(100)
|
||||
|
||||
yield clk.posedge
|
||||
print("test 2: test packet, length %d, zero frame" % payload_len)
|
||||
current_test.next = 2
|
||||
|
||||
enc = cobs_encode(block)
|
||||
|
||||
test_frame = axis_ep.AXIStreamFrame(enc+b'\x00')
|
||||
|
||||
for wait in wait_normal, wait_pause_source, wait_pause_sink:
|
||||
source_queue.put(test_frame)
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
yield wait()
|
||||
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert cobs_decode(enc) == block
|
||||
assert rx_frame.data == block
|
||||
assert not rx_frame.user[-1]
|
||||
|
||||
assert sink_queue.empty()
|
||||
|
||||
yield delay(100)
|
||||
|
||||
yield clk.posedge
|
||||
print("test 3: back-to-back packets, length %d" % payload_len)
|
||||
current_test.next = 3
|
||||
|
||||
test_frame2 = axis_ep.AXIStreamFrame(enc)
|
||||
test_frame1 = axis_ep.AXIStreamFrame(enc)
|
||||
|
||||
for wait in wait_normal, wait_pause_source, wait_pause_sink:
|
||||
source_queue.put(test_frame1)
|
||||
source_queue.put(test_frame2)
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
yield wait()
|
||||
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert cobs_decode(enc) == block
|
||||
assert rx_frame.data == block
|
||||
assert not rx_frame.user[-1]
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert cobs_decode(enc) == block
|
||||
assert rx_frame.data == block
|
||||
assert not rx_frame.user[-1]
|
||||
|
||||
assert sink_queue.empty()
|
||||
|
||||
yield delay(100)
|
||||
|
||||
yield clk.posedge
|
||||
print("test 4: back-to-back packets, length %d, zero frame" % payload_len)
|
||||
current_test.next = 4
|
||||
|
||||
test_frame = axis_ep.AXIStreamFrame(enc+b'\x00'+enc+b'\x00')
|
||||
|
||||
for wait in wait_normal, wait_pause_source, wait_pause_sink:
|
||||
source_queue.put(test_frame)
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
yield wait()
|
||||
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert cobs_decode(enc) == block
|
||||
assert rx_frame.data == block
|
||||
assert not rx_frame.user[-1]
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert cobs_decode(enc) == block
|
||||
assert rx_frame.data == block
|
||||
assert not rx_frame.user[-1]
|
||||
|
||||
assert sink_queue.empty()
|
||||
|
||||
yield delay(100)
|
||||
|
||||
yield clk.posedge
|
||||
print("test 5: tuser assert and bad frame, length %d" % payload_len)
|
||||
current_test.next = 5
|
||||
|
||||
test_frame1 = axis_ep.AXIStreamFrame(enc)
|
||||
test_frame2 = axis_ep.AXIStreamFrame(enc+b'\x02')
|
||||
test_frame3 = axis_ep.AXIStreamFrame(enc)
|
||||
|
||||
test_frame1.user = 1
|
||||
|
||||
for wait in wait_normal, wait_pause_source, wait_pause_sink:
|
||||
source_queue.put(test_frame1)
|
||||
source_queue.put(test_frame2)
|
||||
source_queue.put(test_frame3)
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
yield wait()
|
||||
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert rx_frame.user[-1]
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert rx_frame.user[-1]
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert cobs_decode(enc) == block
|
||||
assert rx_frame.data == block
|
||||
assert not rx_frame.user[-1]
|
||||
|
||||
assert sink_queue.empty()
|
||||
|
||||
yield delay(100)
|
||||
|
||||
yield clk.posedge
|
||||
print("test 6: tuser assert and bad frame, length %d, zero frame" % payload_len)
|
||||
current_test.next = 6
|
||||
|
||||
test_frame1 = axis_ep.AXIStreamFrame(enc+b'\x00')
|
||||
test_frame2 = axis_ep.AXIStreamFrame(enc+b'\x02\x00')
|
||||
test_frame3 = axis_ep.AXIStreamFrame(enc+b'\x00')
|
||||
|
||||
test_frame1.user = 1
|
||||
|
||||
for wait in wait_normal, wait_pause_source, wait_pause_sink:
|
||||
source_queue.put(test_frame1)
|
||||
source_queue.put(test_frame2)
|
||||
source_queue.put(test_frame3)
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
yield wait()
|
||||
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert rx_frame.user[-1]
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert rx_frame.user[-1]
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert cobs_decode(enc) == block
|
||||
assert rx_frame.data == block
|
||||
assert not rx_frame.user[-1]
|
||||
|
||||
assert sink_queue.empty()
|
||||
|
||||
yield delay(100)
|
||||
|
||||
raise StopSimulation
|
||||
|
||||
return dut, source, sink, clkgen, check
|
||||
|
||||
def test_bench():
|
||||
sim = Simulation(bench())
|
||||
sim.run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Running test...")
|
||||
test_bench()
|
91
tb/test_axis_cobs_decode.v
Normal file
91
tb/test_axis_cobs_decode.v
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* Testbench for axis_cobs_decode
|
||||
*/
|
||||
module test_axis_cobs_decode;
|
||||
|
||||
// Parameters
|
||||
|
||||
// Inputs
|
||||
reg clk = 0;
|
||||
reg rst = 0;
|
||||
reg [7:0] current_test = 0;
|
||||
|
||||
reg [7:0] input_axis_tdata = 0;
|
||||
reg input_axis_tvalid = 0;
|
||||
reg input_axis_tlast = 0;
|
||||
reg input_axis_tuser = 0;
|
||||
reg output_axis_tready = 0;
|
||||
|
||||
// Outputs
|
||||
wire input_axis_tready;
|
||||
wire [7:0] output_axis_tdata;
|
||||
wire output_axis_tvalid;
|
||||
wire output_axis_tlast;
|
||||
wire output_axis_tuser;
|
||||
|
||||
initial begin
|
||||
// myhdl integration
|
||||
$from_myhdl(clk,
|
||||
rst,
|
||||
current_test,
|
||||
input_axis_tdata,
|
||||
input_axis_tvalid,
|
||||
input_axis_tlast,
|
||||
input_axis_tuser,
|
||||
output_axis_tready);
|
||||
$to_myhdl(input_axis_tready,
|
||||
output_axis_tdata,
|
||||
output_axis_tvalid,
|
||||
output_axis_tlast,
|
||||
output_axis_tuser);
|
||||
|
||||
// dump file
|
||||
$dumpfile("test_axis_cobs_decode.lxt");
|
||||
$dumpvars(0, test_axis_cobs_decode);
|
||||
end
|
||||
|
||||
axis_cobs_decode
|
||||
UUT (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.input_axis_tdata(input_axis_tdata),
|
||||
.input_axis_tvalid(input_axis_tvalid),
|
||||
.input_axis_tready(input_axis_tready),
|
||||
.input_axis_tlast(input_axis_tlast),
|
||||
.input_axis_tuser(input_axis_tuser),
|
||||
.output_axis_tdata(output_axis_tdata),
|
||||
.output_axis_tvalid(output_axis_tvalid),
|
||||
.output_axis_tready(output_axis_tready),
|
||||
.output_axis_tlast(output_axis_tlast),
|
||||
.output_axis_tuser(output_axis_tuser)
|
||||
);
|
||||
|
||||
endmodule
|
381
tb/test_axis_cobs_encode.py
Executable file
381
tb/test_axis_cobs_encode.py
Executable file
@ -0,0 +1,381 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2016 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
from myhdl import *
|
||||
import os
|
||||
|
||||
try:
|
||||
from queue import Queue
|
||||
except ImportError:
|
||||
from Queue import Queue
|
||||
|
||||
import axis_ep
|
||||
|
||||
module = 'axis_cobs_encode'
|
||||
testbench = 'test_axis_cobs_encode'
|
||||
|
||||
srcs = []
|
||||
|
||||
srcs.append("../rtl/%s.v" % module)
|
||||
srcs.append("../rtl/axis_fifo.v")
|
||||
srcs.append("%s.v" % testbench)
|
||||
|
||||
src = ' '.join(srcs)
|
||||
|
||||
build_cmd = "iverilog -o %s.vvp %s" % (testbench, src)
|
||||
|
||||
def dut_axis_cobs_encode(clk,
|
||||
rst,
|
||||
current_test,
|
||||
input_axis_tdata,
|
||||
input_axis_tvalid,
|
||||
input_axis_tready,
|
||||
input_axis_tlast,
|
||||
input_axis_tuser,
|
||||
output_axis_tdata,
|
||||
output_axis_tvalid,
|
||||
output_axis_tready,
|
||||
output_axis_tlast,
|
||||
output_axis_tuser):
|
||||
|
||||
if os.system(build_cmd):
|
||||
raise Exception("Error running build command")
|
||||
return Cosimulation("vvp -m myhdl %s.vvp -lxt2" % testbench,
|
||||
clk=clk,
|
||||
rst=rst,
|
||||
current_test=current_test,
|
||||
input_axis_tdata=input_axis_tdata,
|
||||
input_axis_tvalid=input_axis_tvalid,
|
||||
input_axis_tready=input_axis_tready,
|
||||
input_axis_tlast=input_axis_tlast,
|
||||
input_axis_tuser=input_axis_tuser,
|
||||
output_axis_tdata=output_axis_tdata,
|
||||
output_axis_tvalid=output_axis_tvalid,
|
||||
output_axis_tready=output_axis_tready,
|
||||
output_axis_tlast=output_axis_tlast,
|
||||
output_axis_tuser=output_axis_tuser)
|
||||
|
||||
def cobs_encode(block):
|
||||
block = bytearray(block)
|
||||
enc = bytearray()
|
||||
|
||||
seg = bytearray()
|
||||
code = 1
|
||||
|
||||
new_data = True
|
||||
|
||||
for b in block:
|
||||
if b == 0:
|
||||
enc.append(code)
|
||||
enc.extend(seg)
|
||||
code = 1
|
||||
seg = bytearray()
|
||||
new_data = True
|
||||
else:
|
||||
code += 1
|
||||
seg.append(b)
|
||||
new_data = True
|
||||
if code == 255:
|
||||
enc.append(code)
|
||||
enc.extend(seg)
|
||||
code = 1
|
||||
seg = bytearray()
|
||||
new_data = False
|
||||
|
||||
if new_data:
|
||||
enc.append(code)
|
||||
enc.extend(seg)
|
||||
|
||||
return bytes(enc)
|
||||
|
||||
def cobs_decode(block):
|
||||
block = bytearray(block)
|
||||
dec = bytearray()
|
||||
|
||||
it = iter(bytearray(block))
|
||||
code = 0
|
||||
|
||||
i = 0
|
||||
|
||||
if 0 in block:
|
||||
return None
|
||||
|
||||
while i < len(block):
|
||||
code = block[i]
|
||||
i += 1
|
||||
if i+code-1 > len(block):
|
||||
return None
|
||||
for k in range(code-1):
|
||||
dec.append(block[i])
|
||||
i += 1
|
||||
if code < 255 and i < len(block):
|
||||
dec.append(0)
|
||||
|
||||
return bytes(dec)
|
||||
|
||||
def prbs31(state = 0x7fffffff):
|
||||
while True:
|
||||
for i in range(8):
|
||||
if bool(state & 0x08000000) ^ bool(state & 0x40000000):
|
||||
state = ((state & 0x3fffffff) << 1) | 1
|
||||
else:
|
||||
state = (state & 0x3fffffff) << 1
|
||||
yield state & 0xff
|
||||
|
||||
def bench():
|
||||
|
||||
# Parameters
|
||||
APPEND_ZERO = 0
|
||||
|
||||
# Inputs
|
||||
clk = Signal(bool(0))
|
||||
rst = Signal(bool(0))
|
||||
current_test = Signal(intbv(0)[8:])
|
||||
|
||||
input_axis_tdata = Signal(intbv(0)[8:])
|
||||
input_axis_tvalid = Signal(bool(0))
|
||||
input_axis_tlast = Signal(bool(0))
|
||||
input_axis_tuser = Signal(bool(0))
|
||||
output_axis_tready = Signal(bool(0))
|
||||
|
||||
# Outputs
|
||||
input_axis_tready = Signal(bool(0))
|
||||
output_axis_tdata = Signal(intbv(0)[8:])
|
||||
output_axis_tvalid = Signal(bool(0))
|
||||
output_axis_tlast = Signal(bool(0))
|
||||
output_axis_tuser = Signal(bool(0))
|
||||
|
||||
# sources and sinks
|
||||
source_queue = Queue()
|
||||
source_pause = Signal(bool(0))
|
||||
sink_queue = Queue()
|
||||
sink_pause = Signal(bool(0))
|
||||
|
||||
source = axis_ep.AXIStreamSource(clk,
|
||||
rst,
|
||||
tdata=input_axis_tdata,
|
||||
tvalid=input_axis_tvalid,
|
||||
tready=input_axis_tready,
|
||||
tlast=input_axis_tlast,
|
||||
tuser=input_axis_tuser,
|
||||
fifo=source_queue,
|
||||
pause=source_pause,
|
||||
name='source')
|
||||
|
||||
sink = axis_ep.AXIStreamSink(clk,
|
||||
rst,
|
||||
tdata=output_axis_tdata,
|
||||
tvalid=output_axis_tvalid,
|
||||
tready=output_axis_tready,
|
||||
tlast=output_axis_tlast,
|
||||
tuser=output_axis_tuser,
|
||||
fifo=sink_queue,
|
||||
pause=sink_pause,
|
||||
name='sink')
|
||||
|
||||
# DUT
|
||||
dut = dut_axis_cobs_encode(clk,
|
||||
rst,
|
||||
current_test,
|
||||
input_axis_tdata,
|
||||
input_axis_tvalid,
|
||||
input_axis_tready,
|
||||
input_axis_tlast,
|
||||
input_axis_tuser,
|
||||
output_axis_tdata,
|
||||
output_axis_tvalid,
|
||||
output_axis_tready,
|
||||
output_axis_tlast,
|
||||
output_axis_tuser)
|
||||
|
||||
@always(delay(4))
|
||||
def clkgen():
|
||||
clk.next = not clk
|
||||
|
||||
def wait_normal():
|
||||
i = 4
|
||||
while i > 0:
|
||||
i = max(0, i-1)
|
||||
if input_axis_tvalid or output_axis_tvalid or not source_queue.empty():
|
||||
i = 4
|
||||
yield clk.posedge
|
||||
|
||||
def wait_pause_source():
|
||||
i = 2
|
||||
while i > 0:
|
||||
i = max(0, i-1)
|
||||
if input_axis_tvalid or output_axis_tvalid or not source_queue.empty():
|
||||
i = 2
|
||||
source_pause.next = True
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
source_pause.next = False
|
||||
yield clk.posedge
|
||||
|
||||
def wait_pause_sink():
|
||||
i = 2
|
||||
while i > 0:
|
||||
i = max(0, i-1)
|
||||
if input_axis_tvalid or output_axis_tvalid or not source_queue.empty():
|
||||
i = 2
|
||||
sink_pause.next = True
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
sink_pause.next = False
|
||||
yield clk.posedge
|
||||
|
||||
@instance
|
||||
def check():
|
||||
yield delay(100)
|
||||
yield clk.posedge
|
||||
rst.next = 1
|
||||
yield clk.posedge
|
||||
rst.next = 0
|
||||
yield clk.posedge
|
||||
yield delay(100)
|
||||
yield clk.posedge
|
||||
|
||||
# testbench stimulus
|
||||
|
||||
for payload_len in list(range(1,33))+list(range(252,261))+[512,1024]:
|
||||
gen = prbs31()
|
||||
for block in [bytearray([0]*payload_len),
|
||||
bytearray([k%255+1 for k in range(payload_len)]),
|
||||
b'\x00'+bytearray([k%255+1 for k in range(payload_len)])+b'\x00',
|
||||
bytearray([next(gen) for i in range(payload_len)])]:
|
||||
yield clk.posedge
|
||||
print("test 1: test packet, length %d" % payload_len)
|
||||
current_test.next = 1
|
||||
|
||||
enc = cobs_encode(block)
|
||||
|
||||
test_frame = axis_ep.AXIStreamFrame(block)
|
||||
|
||||
for wait in wait_normal, wait_pause_source, wait_pause_sink:
|
||||
source_queue.put(test_frame)
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
yield wait()
|
||||
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert cobs_decode(enc) == block
|
||||
assert rx_frame.data == enc
|
||||
assert cobs_decode(rx_frame.data) == block
|
||||
assert not rx_frame.user[-1]
|
||||
|
||||
assert sink_queue.empty()
|
||||
|
||||
yield delay(100)
|
||||
|
||||
yield clk.posedge
|
||||
print("test 2: back-to-back packets, length %d" % payload_len)
|
||||
current_test.next = 2
|
||||
|
||||
test_frame1 = axis_ep.AXIStreamFrame(block)
|
||||
test_frame2 = axis_ep.AXIStreamFrame(block)
|
||||
|
||||
for wait in wait_normal, wait_pause_source, wait_pause_sink:
|
||||
source_queue.put(test_frame1)
|
||||
source_queue.put(test_frame2)
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
yield wait()
|
||||
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert cobs_decode(enc) == block
|
||||
assert rx_frame.data == enc
|
||||
assert cobs_decode(rx_frame.data) == block
|
||||
assert not rx_frame.user[-1]
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert cobs_decode(enc) == block
|
||||
assert rx_frame.data == enc
|
||||
assert cobs_decode(rx_frame.data) == block
|
||||
assert not rx_frame.user[-1]
|
||||
|
||||
assert sink_queue.empty()
|
||||
|
||||
yield delay(100)
|
||||
|
||||
yield clk.posedge
|
||||
print("test 3: tuser assert, length %d" % payload_len)
|
||||
current_test.next = 3
|
||||
|
||||
test_frame1 = axis_ep.AXIStreamFrame(block)
|
||||
test_frame2 = axis_ep.AXIStreamFrame(block)
|
||||
|
||||
test_frame1.user = 1
|
||||
|
||||
for wait in wait_normal, wait_pause_source, wait_pause_sink:
|
||||
source_queue.put(test_frame1)
|
||||
source_queue.put(test_frame2)
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
yield wait()
|
||||
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert cobs_decode(rx_frame.data) == None
|
||||
assert rx_frame.user[-1]
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert cobs_decode(enc) == block
|
||||
assert rx_frame.data == enc
|
||||
assert cobs_decode(rx_frame.data) == block
|
||||
assert not rx_frame.user[-1]
|
||||
|
||||
assert sink_queue.empty()
|
||||
|
||||
yield delay(100)
|
||||
|
||||
raise StopSimulation
|
||||
|
||||
return dut, source, sink, clkgen, check
|
||||
|
||||
def test_bench():
|
||||
sim = Simulation(bench())
|
||||
sim.run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Running test...")
|
||||
test_bench()
|
94
tb/test_axis_cobs_encode.v
Normal file
94
tb/test_axis_cobs_encode.v
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* Testbench for axis_cobs_encode
|
||||
*/
|
||||
module test_axis_cobs_encode;
|
||||
|
||||
// Parameters
|
||||
parameter APPEND_ZERO = 0;
|
||||
|
||||
// Inputs
|
||||
reg clk = 0;
|
||||
reg rst = 0;
|
||||
reg [7:0] current_test = 0;
|
||||
|
||||
reg [7:0] input_axis_tdata = 0;
|
||||
reg input_axis_tvalid = 0;
|
||||
reg input_axis_tlast = 0;
|
||||
reg input_axis_tuser = 0;
|
||||
reg output_axis_tready = 0;
|
||||
|
||||
// Outputs
|
||||
wire input_axis_tready;
|
||||
wire [7:0] output_axis_tdata;
|
||||
wire output_axis_tvalid;
|
||||
wire output_axis_tlast;
|
||||
wire output_axis_tuser;
|
||||
|
||||
initial begin
|
||||
// myhdl integration
|
||||
$from_myhdl(clk,
|
||||
rst,
|
||||
current_test,
|
||||
input_axis_tdata,
|
||||
input_axis_tvalid,
|
||||
input_axis_tlast,
|
||||
input_axis_tuser,
|
||||
output_axis_tready);
|
||||
$to_myhdl(input_axis_tready,
|
||||
output_axis_tdata,
|
||||
output_axis_tvalid,
|
||||
output_axis_tlast,
|
||||
output_axis_tuser);
|
||||
|
||||
// dump file
|
||||
$dumpfile("test_axis_cobs_encode.lxt");
|
||||
$dumpvars(0, test_axis_cobs_encode);
|
||||
end
|
||||
|
||||
axis_cobs_encode #(
|
||||
.APPEND_ZERO(APPEND_ZERO)
|
||||
)
|
||||
UUT (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.input_axis_tdata(input_axis_tdata),
|
||||
.input_axis_tvalid(input_axis_tvalid),
|
||||
.input_axis_tready(input_axis_tready),
|
||||
.input_axis_tlast(input_axis_tlast),
|
||||
.input_axis_tuser(input_axis_tuser),
|
||||
.output_axis_tdata(output_axis_tdata),
|
||||
.output_axis_tvalid(output_axis_tvalid),
|
||||
.output_axis_tready(output_axis_tready),
|
||||
.output_axis_tlast(output_axis_tlast),
|
||||
.output_axis_tuser(output_axis_tuser)
|
||||
);
|
||||
|
||||
endmodule
|
381
tb/test_axis_cobs_encode_zero_frame.py
Executable file
381
tb/test_axis_cobs_encode_zero_frame.py
Executable file
@ -0,0 +1,381 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
|
||||
Copyright (c) 2016 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
"""
|
||||
|
||||
from myhdl import *
|
||||
import os
|
||||
|
||||
try:
|
||||
from queue import Queue
|
||||
except ImportError:
|
||||
from Queue import Queue
|
||||
|
||||
import axis_ep
|
||||
|
||||
module = 'axis_cobs_encode'
|
||||
testbench = 'test_axis_cobs_encode_zero_frame'
|
||||
|
||||
srcs = []
|
||||
|
||||
srcs.append("../rtl/%s.v" % module)
|
||||
srcs.append("../rtl/axis_fifo.v")
|
||||
srcs.append("%s.v" % testbench)
|
||||
|
||||
src = ' '.join(srcs)
|
||||
|
||||
build_cmd = "iverilog -o %s.vvp %s" % (testbench, src)
|
||||
|
||||
def dut_axis_cobs_encode(clk,
|
||||
rst,
|
||||
current_test,
|
||||
input_axis_tdata,
|
||||
input_axis_tvalid,
|
||||
input_axis_tready,
|
||||
input_axis_tlast,
|
||||
input_axis_tuser,
|
||||
output_axis_tdata,
|
||||
output_axis_tvalid,
|
||||
output_axis_tready,
|
||||
output_axis_tlast,
|
||||
output_axis_tuser):
|
||||
|
||||
if os.system(build_cmd):
|
||||
raise Exception("Error running build command")
|
||||
return Cosimulation("vvp -m myhdl %s.vvp -lxt2" % testbench,
|
||||
clk=clk,
|
||||
rst=rst,
|
||||
current_test=current_test,
|
||||
input_axis_tdata=input_axis_tdata,
|
||||
input_axis_tvalid=input_axis_tvalid,
|
||||
input_axis_tready=input_axis_tready,
|
||||
input_axis_tlast=input_axis_tlast,
|
||||
input_axis_tuser=input_axis_tuser,
|
||||
output_axis_tdata=output_axis_tdata,
|
||||
output_axis_tvalid=output_axis_tvalid,
|
||||
output_axis_tready=output_axis_tready,
|
||||
output_axis_tlast=output_axis_tlast,
|
||||
output_axis_tuser=output_axis_tuser)
|
||||
|
||||
def cobs_encode(block):
|
||||
block = bytearray(block)
|
||||
enc = bytearray()
|
||||
|
||||
seg = bytearray()
|
||||
code = 1
|
||||
|
||||
new_data = True
|
||||
|
||||
for b in block:
|
||||
if b == 0:
|
||||
enc.append(code)
|
||||
enc.extend(seg)
|
||||
code = 1
|
||||
seg = bytearray()
|
||||
new_data = True
|
||||
else:
|
||||
code += 1
|
||||
seg.append(b)
|
||||
new_data = True
|
||||
if code == 255:
|
||||
enc.append(code)
|
||||
enc.extend(seg)
|
||||
code = 1
|
||||
seg = bytearray()
|
||||
new_data = False
|
||||
|
||||
if new_data:
|
||||
enc.append(code)
|
||||
enc.extend(seg)
|
||||
|
||||
return bytes(enc)
|
||||
|
||||
def cobs_decode(block):
|
||||
block = bytearray(block)
|
||||
dec = bytearray()
|
||||
|
||||
it = iter(bytearray(block))
|
||||
code = 0
|
||||
|
||||
i = 0
|
||||
|
||||
if 0 in block:
|
||||
return None
|
||||
|
||||
while i < len(block):
|
||||
code = block[i]
|
||||
i += 1
|
||||
if i+code-1 > len(block):
|
||||
return None
|
||||
for k in range(code-1):
|
||||
dec.append(block[i])
|
||||
i += 1
|
||||
if code < 255 and i < len(block):
|
||||
dec.append(0)
|
||||
|
||||
return bytes(dec)
|
||||
|
||||
def prbs31(state = 0x7fffffff):
|
||||
while True:
|
||||
for i in range(8):
|
||||
if bool(state & 0x08000000) ^ bool(state & 0x40000000):
|
||||
state = ((state & 0x3fffffff) << 1) | 1
|
||||
else:
|
||||
state = (state & 0x3fffffff) << 1
|
||||
yield state & 0xff
|
||||
|
||||
def bench():
|
||||
|
||||
# Parameters
|
||||
APPEND_ZERO = 1
|
||||
|
||||
# Inputs
|
||||
clk = Signal(bool(0))
|
||||
rst = Signal(bool(0))
|
||||
current_test = Signal(intbv(0)[8:])
|
||||
|
||||
input_axis_tdata = Signal(intbv(0)[8:])
|
||||
input_axis_tvalid = Signal(bool(0))
|
||||
input_axis_tlast = Signal(bool(0))
|
||||
input_axis_tuser = Signal(bool(0))
|
||||
output_axis_tready = Signal(bool(0))
|
||||
|
||||
# Outputs
|
||||
input_axis_tready = Signal(bool(0))
|
||||
output_axis_tdata = Signal(intbv(0)[8:])
|
||||
output_axis_tvalid = Signal(bool(0))
|
||||
output_axis_tlast = Signal(bool(0))
|
||||
output_axis_tuser = Signal(bool(0))
|
||||
|
||||
# sources and sinks
|
||||
source_queue = Queue()
|
||||
source_pause = Signal(bool(0))
|
||||
sink_queue = Queue()
|
||||
sink_pause = Signal(bool(0))
|
||||
|
||||
source = axis_ep.AXIStreamSource(clk,
|
||||
rst,
|
||||
tdata=input_axis_tdata,
|
||||
tvalid=input_axis_tvalid,
|
||||
tready=input_axis_tready,
|
||||
tlast=input_axis_tlast,
|
||||
tuser=input_axis_tuser,
|
||||
fifo=source_queue,
|
||||
pause=source_pause,
|
||||
name='source')
|
||||
|
||||
sink = axis_ep.AXIStreamSink(clk,
|
||||
rst,
|
||||
tdata=output_axis_tdata,
|
||||
tvalid=output_axis_tvalid,
|
||||
tready=output_axis_tready,
|
||||
tlast=output_axis_tlast,
|
||||
tuser=output_axis_tuser,
|
||||
fifo=sink_queue,
|
||||
pause=sink_pause,
|
||||
name='sink')
|
||||
|
||||
# DUT
|
||||
dut = dut_axis_cobs_encode(clk,
|
||||
rst,
|
||||
current_test,
|
||||
input_axis_tdata,
|
||||
input_axis_tvalid,
|
||||
input_axis_tready,
|
||||
input_axis_tlast,
|
||||
input_axis_tuser,
|
||||
output_axis_tdata,
|
||||
output_axis_tvalid,
|
||||
output_axis_tready,
|
||||
output_axis_tlast,
|
||||
output_axis_tuser)
|
||||
|
||||
@always(delay(4))
|
||||
def clkgen():
|
||||
clk.next = not clk
|
||||
|
||||
def wait_normal():
|
||||
i = 4
|
||||
while i > 0:
|
||||
i = max(0, i-1)
|
||||
if input_axis_tvalid or output_axis_tvalid or not source_queue.empty():
|
||||
i = 4
|
||||
yield clk.posedge
|
||||
|
||||
def wait_pause_source():
|
||||
i = 2
|
||||
while i > 0:
|
||||
i = max(0, i-1)
|
||||
if input_axis_tvalid or output_axis_tvalid or not source_queue.empty():
|
||||
i = 2
|
||||
source_pause.next = True
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
source_pause.next = False
|
||||
yield clk.posedge
|
||||
|
||||
def wait_pause_sink():
|
||||
i = 2
|
||||
while i > 0:
|
||||
i = max(0, i-1)
|
||||
if input_axis_tvalid or output_axis_tvalid or not source_queue.empty():
|
||||
i = 2
|
||||
sink_pause.next = True
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
sink_pause.next = False
|
||||
yield clk.posedge
|
||||
|
||||
@instance
|
||||
def check():
|
||||
yield delay(100)
|
||||
yield clk.posedge
|
||||
rst.next = 1
|
||||
yield clk.posedge
|
||||
rst.next = 0
|
||||
yield clk.posedge
|
||||
yield delay(100)
|
||||
yield clk.posedge
|
||||
|
||||
# testbench stimulus
|
||||
|
||||
for payload_len in list(range(1,33))+list(range(252,261))+[512,1024]:
|
||||
gen = prbs31()
|
||||
for block in [bytearray([0]*payload_len),
|
||||
bytearray([k%255+1 for k in range(payload_len)]),
|
||||
b'\x00'+bytearray([k%255+1 for k in range(payload_len)])+b'\x00',
|
||||
bytearray([next(gen) for i in range(payload_len)])]:
|
||||
yield clk.posedge
|
||||
print("test 1: test packet, length %d" % payload_len)
|
||||
current_test.next = 1
|
||||
|
||||
enc = cobs_encode(block)
|
||||
|
||||
test_frame = axis_ep.AXIStreamFrame(block)
|
||||
|
||||
for wait in wait_normal, wait_pause_source, wait_pause_sink:
|
||||
source_queue.put(test_frame)
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
yield wait()
|
||||
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert cobs_decode(enc) == block
|
||||
assert rx_frame.data == enc+b'\x00'
|
||||
assert cobs_decode(rx_frame.data[:-1]) == block
|
||||
assert not rx_frame.user[-1]
|
||||
|
||||
assert sink_queue.empty()
|
||||
|
||||
yield delay(100)
|
||||
|
||||
yield clk.posedge
|
||||
print("test 2: back-to-back packets, length %d" % payload_len)
|
||||
current_test.next = 2
|
||||
|
||||
test_frame1 = axis_ep.AXIStreamFrame(block)
|
||||
test_frame2 = axis_ep.AXIStreamFrame(block)
|
||||
|
||||
for wait in wait_normal, wait_pause_source, wait_pause_sink:
|
||||
source_queue.put(test_frame1)
|
||||
source_queue.put(test_frame2)
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
yield wait()
|
||||
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert cobs_decode(enc) == block
|
||||
assert rx_frame.data == enc+b'\x00'
|
||||
assert cobs_decode(rx_frame.data[:-1]) == block
|
||||
assert not rx_frame.user[-1]
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert cobs_decode(enc) == block
|
||||
assert rx_frame.data == enc+b'\x00'
|
||||
assert cobs_decode(rx_frame.data[:-1]) == block
|
||||
assert not rx_frame.user[-1]
|
||||
|
||||
assert sink_queue.empty()
|
||||
|
||||
yield delay(100)
|
||||
|
||||
yield clk.posedge
|
||||
print("test 3: tuser assert, length %d" % payload_len)
|
||||
current_test.next = 3
|
||||
|
||||
test_frame1 = axis_ep.AXIStreamFrame(block)
|
||||
test_frame2 = axis_ep.AXIStreamFrame(block)
|
||||
|
||||
test_frame1.user = 1
|
||||
|
||||
for wait in wait_normal, wait_pause_source, wait_pause_sink:
|
||||
source_queue.put(test_frame1)
|
||||
source_queue.put(test_frame2)
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
yield wait()
|
||||
|
||||
yield clk.posedge
|
||||
yield clk.posedge
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert cobs_decode(rx_frame.data[:-1]) == None
|
||||
assert rx_frame.user[-1]
|
||||
|
||||
rx_frame = sink_queue.get(False)
|
||||
|
||||
assert cobs_decode(enc) == block
|
||||
assert rx_frame.data == enc+b'\x00'
|
||||
assert cobs_decode(rx_frame.data[:-1]) == block
|
||||
assert not rx_frame.user[-1]
|
||||
|
||||
assert sink_queue.empty()
|
||||
|
||||
yield delay(100)
|
||||
|
||||
raise StopSimulation
|
||||
|
||||
return dut, source, sink, clkgen, check
|
||||
|
||||
def test_bench():
|
||||
sim = Simulation(bench())
|
||||
sim.run()
|
||||
|
||||
if __name__ == '__main__':
|
||||
print("Running test...")
|
||||
test_bench()
|
94
tb/test_axis_cobs_encode_zero_frame.v
Normal file
94
tb/test_axis_cobs_encode_zero_frame.v
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2016 Alex Forencich
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
// Language: Verilog 2001
|
||||
|
||||
`timescale 1ns / 1ps
|
||||
|
||||
/*
|
||||
* Testbench for axis_cobs_encode
|
||||
*/
|
||||
module test_axis_cobs_encode_zero_frame;
|
||||
|
||||
// Parameters
|
||||
parameter APPEND_ZERO = 1;
|
||||
|
||||
// Inputs
|
||||
reg clk = 0;
|
||||
reg rst = 0;
|
||||
reg [7:0] current_test = 0;
|
||||
|
||||
reg [7:0] input_axis_tdata = 0;
|
||||
reg input_axis_tvalid = 0;
|
||||
reg input_axis_tlast = 0;
|
||||
reg input_axis_tuser = 0;
|
||||
reg output_axis_tready = 0;
|
||||
|
||||
// Outputs
|
||||
wire input_axis_tready;
|
||||
wire [7:0] output_axis_tdata;
|
||||
wire output_axis_tvalid;
|
||||
wire output_axis_tlast;
|
||||
wire output_axis_tuser;
|
||||
|
||||
initial begin
|
||||
// myhdl integration
|
||||
$from_myhdl(clk,
|
||||
rst,
|
||||
current_test,
|
||||
input_axis_tdata,
|
||||
input_axis_tvalid,
|
||||
input_axis_tlast,
|
||||
input_axis_tuser,
|
||||
output_axis_tready);
|
||||
$to_myhdl(input_axis_tready,
|
||||
output_axis_tdata,
|
||||
output_axis_tvalid,
|
||||
output_axis_tlast,
|
||||
output_axis_tuser);
|
||||
|
||||
// dump file
|
||||
$dumpfile("test_axis_cobs_encode_zero_frame.lxt");
|
||||
$dumpvars(0, test_axis_cobs_encode_zero_frame);
|
||||
end
|
||||
|
||||
axis_cobs_encode #(
|
||||
.APPEND_ZERO(APPEND_ZERO)
|
||||
)
|
||||
UUT (
|
||||
.clk(clk),
|
||||
.rst(rst),
|
||||
.input_axis_tdata(input_axis_tdata),
|
||||
.input_axis_tvalid(input_axis_tvalid),
|
||||
.input_axis_tready(input_axis_tready),
|
||||
.input_axis_tlast(input_axis_tlast),
|
||||
.input_axis_tuser(input_axis_tuser),
|
||||
.output_axis_tdata(output_axis_tdata),
|
||||
.output_axis_tvalid(output_axis_tvalid),
|
||||
.output_axis_tready(output_axis_tready),
|
||||
.output_axis_tlast(output_axis_tlast),
|
||||
.output_axis_tuser(output_axis_tuser)
|
||||
);
|
||||
|
||||
endmodule
|
Loading…
x
Reference in New Issue
Block a user