diff --git a/Bender.yml b/Bender.yml index 3b8996b9b..a695fb8a4 100644 --- a/Bender.yml +++ b/Bender.yml @@ -40,6 +40,7 @@ sources: - src/axi_join.sv - src/axi_lite_demux.sv - src/axi_lite_join.sv + - src/axi_lite_lfsr.sv - src/axi_lite_mailbox.sv - src/axi_lite_mux.sv - src/axi_lite_regs.sv @@ -55,6 +56,7 @@ sources: - src/axi_err_slv.sv - src/axi_dw_converter.sv - src/axi_id_serialize.sv + - src/axi_lfsr.sv - src/axi_multicut.sv - src/axi_to_axi_lite.sv - src/axi_to_mem_banked.sv diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f83aa159..4f2faa32a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Add `axi_dumper` and `axi_dumper_interpret` script to dump log from an AXI bus for debugging purposes. - Add FuseSoC and Vivado XSIM limited test to CI - `assign.svh`: Add macros to assign flat buses using the Vivado naming style. +- `axi_lfsr` and `axi_lite_lfsr`: Add AXI4 and AXI4 Lite LFSR Subordinate devices. ### Changed - Improve compatibility with FuseSoC diff --git a/README.md b/README.md index 5daef26f8..ed678dfba 100644 --- a/README.md +++ b/README.md @@ -39,8 +39,10 @@ In addition to the documents linked in the following table, we are setting up [d | [`axi_isolate`](src/axi_isolate.sv) | A module that can isolate downstream slaves from receiving new AXI4 transactions. | | | [`axi_iw_converter`](src/axi_iw_converter.sv) | Convert between any two AXI ID widths. | [Doc][doc.axi_iw_converter] | | [`axi_join`](src/axi_join.sv) | A connector that joins two AXI interfaces. | | +| [`axi_lfsr`](src/axi_lfsr.sv) | AXI4-attached LFSR; read returns pseudo-random data, writes are compressed into a checksum. | | | [`axi_lite_demux`](src/axi_lite_demux.sv) | Demultiplexes an AXI4-Lite bus from one slave port to multiple master ports. | [Doc](doc/axi_lite_demux.md) | | [`axi_lite_join`](src/axi_lite_join.sv) | A connector that joins two AXI-Lite interfaces. | | +| [`axi_lite_lfsr`](src/axi_lite_lfsr.sv) | AXI4-Lite-attached LFSR; read returns pseudo-random data, writes are compressed into a checksum. | | | [`axi_lite_mailbox`](src/axi_lite_mailbox.sv) | A AXI4-Lite Mailbox with two slave ports and usage triggered irq. | [Doc](doc/axi_lite_mailbox.md) | | [`axi_lite_mux`](src/axi_lite_mux.sv) | Multiplexes AXI4-Lite slave ports down to one master port. | [Doc](doc/axi_lite_mux.md) | | [`axi_lite_regs`](src/axi_lite_regs.sv) | AXI4-Lite registers with optional read-only and protection features. | [Doc][doc.axi_lite_regs] | diff --git a/axi.core b/axi.core index 7e487dffd..3282dbee3 100644 --- a/axi.core +++ b/axi.core @@ -31,6 +31,7 @@ filesets: - src/axi_join.sv - src/axi_lite_demux.sv - src/axi_lite_join.sv + - src/axi_lite_lfsr.sv - src/axi_lite_mailbox.sv - src/axi_lite_mux.sv - src/axi_lite_regs.sv @@ -46,6 +47,7 @@ filesets: - src/axi_err_slv.sv - src/axi_dw_converter.sv - src/axi_id_serialize.sv + - src/axi_lfsr.sv - src/axi_multicut.sv - src/axi_to_axi_lite.sv - src/axi_to_mem_banked.sv diff --git a/src/axi_lfsr.sv b/src/axi_lfsr.sv new file mode 100644 index 000000000..2085a76fe --- /dev/null +++ b/src/axi_lfsr.sv @@ -0,0 +1,121 @@ +// Copyright 2022 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Thomas Benz + +`include "axi/typedef.svh" + +/// AXI4 LFSR Subordinate device. Responds with a pseudo random answer. Serial interface to +/// set the internal state. +module axi_lfsr #( + /// AXI4 Data Width + parameter int unsigned DataWidth = 32'd0, + /// AXI4 Addr Width + parameter int unsigned AddrWidth = 32'd0, + /// AXI4 Id Width + parameter int unsigned IdWidth = 32'd0, + /// AXI4 User Width + parameter int unsigned UserWidth = 32'd0, + /// AXI4 request struct definition + parameter type axi_req_t = logic, + /// AXI4 response struct definition + parameter type axi_rsp_t = logic +)( + /// Rising-edge clock + input logic clk_i, + /// Active-low reset + input logic rst_ni, + /// Testmode + input logic testmode_i, + /// AXI4 request struct + input axi_req_t req_i, + /// AXI4 response struct + output axi_rsp_t rsp_o, + /// Serial shift data in (write) + input logic w_ser_data_i, + /// Serial shift data out (write) + output logic w_ser_data_o, + /// Serial shift enable (write) + input logic w_ser_en_i, + /// Serial shift data in (read) + input logic r_ser_data_i, + /// Serial shift data out (read) + output logic r_ser_data_o, + /// Serial shift enable (read) + input logic r_ser_en_i +); + + /// AXI4 Strobe Width + localparam int unsigned StrbWidth = DataWidth / 8; + + /// Address Type + typedef logic [AddrWidth-1:0] addr_t; + /// Data type + typedef logic [DataWidth-1:0] data_t; + /// Strobe Type + typedef logic [StrbWidth-1:0] strb_t; + + // AXI Lite typedef + `AXI_LITE_TYPEDEF_AW_CHAN_T(axi_lite_aw_chan_t, addr_t) + `AXI_LITE_TYPEDEF_W_CHAN_T(axi_lite_w_chan_t, data_t, strb_t) + `AXI_LITE_TYPEDEF_B_CHAN_T(axi_lite_b_chan_t) + + `AXI_LITE_TYPEDEF_AR_CHAN_T(axi_lite_ar_chan_t, addr_t) + `AXI_LITE_TYPEDEF_R_CHAN_T(axi_lite_r_chan_t, data_t) + + `AXI_LITE_TYPEDEF_REQ_T(axi_lite_req_t, axi_lite_aw_chan_t, axi_lite_w_chan_t, axi_lite_ar_chan_t) + `AXI_LITE_TYPEDEF_RESP_T(axi_lite_rsp_t, axi_lite_b_chan_t, axi_lite_r_chan_t) + + // AXI Lite buses + axi_lite_req_t axi_lite_req; + axi_lite_rsp_t axi_lite_rsp; + + axi_to_axi_lite #( + .AxiAddrWidth ( AddrWidth ), + .AxiDataWidth ( DataWidth ), + .AxiIdWidth ( IdWidth ), + .AxiUserWidth ( UserWidth ), + .AxiMaxWriteTxns ( 'd2 ), // We only have 1 cycle latency; 2 is enough + .AxiMaxReadTxns ( 'd2 ), // We only have 1 cycle latency; 2 is enough + .FallThrough ( 1'b0 ), + .full_req_t ( axi_req_t ), + .full_resp_t ( axi_rsp_t ), + .lite_req_t ( axi_lite_req_t ), + .lite_resp_t ( axi_lite_rsp_t ) + ) i_axi_to_axi_lite ( + .clk_i, + .rst_ni, + .test_i ( testmode_i ), + .slv_req_i ( req_i ), + .slv_resp_o ( rsp_o ), + .mst_req_o ( axi_lite_req ), + .mst_resp_i ( axi_lite_rsp ) + ); + + axi_lite_lfsr #( + .DataWidth ( DataWidth ), + .axi_lite_req_t ( axi_lite_req_t ), + .axi_lite_rsp_t ( axi_lite_rsp_t ) + ) i_axi_lite_lfsr ( + .clk_i, + .rst_ni, + .testmode_i, + .w_ser_data_i, + .w_ser_data_o, + .w_ser_en_i, + .r_ser_data_i, + .r_ser_data_o, + .r_ser_en_i, + .req_i ( axi_lite_req ), + .rsp_o ( axi_lite_rsp ) + ); + +endmodule : axi_lfsr diff --git a/src/axi_lite_lfsr.sv b/src/axi_lite_lfsr.sv new file mode 100644 index 000000000..bf8e0f5e5 --- /dev/null +++ b/src/axi_lite_lfsr.sv @@ -0,0 +1,223 @@ +// Copyright 2022 ETH Zurich and University of Bologna. +// Copyright and related rights are licensed under the Solderpad Hardware +// License, Version 0.51 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law +// or agreed to in writing, software, hardware and materials distributed under +// this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +// CONDITIONS OF ANY KIND, either express or implied. See the License for the +// specific language governing permissions and limitations under the License. +// +// Authors: +// - Thomas Benz + +`include "common_cells/registers.svh" + +/// AXI4 Lite LFSR Subordinate device. Responds with a pseudo random answer. Serial interface to +/// set the internal state. +module axi_lite_lfsr #( + /// AXI4 Lite Data Width + parameter int unsigned DataWidth = 32'd0, + /// AXI4 Lite request struct definition + parameter type axi_lite_req_t = logic, + /// AXI4 Lite response struct definition + parameter type axi_lite_rsp_t = logic +)( + /// Rising-edge clock + input logic clk_i, + /// Active-low reset + input logic rst_ni, + /// Testmode + input logic testmode_i, + /// AXI4 Lite request struct + input axi_lite_req_t req_i, + /// AXI4 Lite response struct + output axi_lite_rsp_t rsp_o, + /// Serial shift data in (write) + input logic w_ser_data_i, + /// Serial shift data out (write) + output logic w_ser_data_o, + /// Serial shift enable (write) + input logic w_ser_en_i, + /// Serial shift data in (read) + input logic r_ser_data_i, + /// Serial shift data out (read) + output logic r_ser_data_o, + /// Serial shift enable (read) + input logic r_ser_en_i +); + + /// AXI4 Strobe Width + localparam int unsigned StrbWidth = DataWidth / 8; + + logic w_lfsr_en; + logic r_lfsr_en; + + logic w_b_fifo_ready; + + // LFSR outputs + logic [DataWidth-1:0] w_data_in, w_data_out; + + // AW (ignored) + assign rsp_o.aw_ready = !w_ser_en_i; + + // W + axi_opt_lfsr #( + .Width ( DataWidth ) + ) i_axi_opt_lfsr_w ( + .clk_i, + .rst_ni, + .en_i ( w_lfsr_en ), + .ser_data_i ( w_ser_data_i ), + .ser_data_o ( w_ser_data_o ), + .ser_en_i ( w_ser_en_i ), + .inp_en_i ( w_lfsr_en ), + .data_i ( w_data_in ), + .data_o ( w_data_out ) + ); + assign w_lfsr_en = req_i.w_valid & rsp_o.w_ready; + assign rsp_o.w_ready = !w_ser_en_i & w_b_fifo_ready; + + // only write bytes with strobe signal enabled + always_comb begin : gen_data_strb_connect + for (int unsigned i = 0; i < StrbWidth; i++) begin : gen_strb_en + if (req_i.w.strb[i] == 1'b0) begin + w_data_in[i*8+:8] = w_data_out[i*8+:8]; + end else if (req_i.w.strb[i] == 1'b1) begin + w_data_in[i*8+:8] = req_i.w.data[i*8+:8]; + end else begin + w_data_in[i*8+:8] = 'x; + end + end + end + + // B + stream_fifo #( + .FALL_THROUGH ( 1'b0 ), + .DATA_WIDTH ( 'd1 ), + .DEPTH ( 'd2 ) + ) i_stream_fifo_w_b ( + .clk_i, + .rst_ni, + .testmode_i, + .flush_i ( 1'b0 ), + .usage_o ( /* NOT CONNECTED */ ), + .data_i ( 1'b0 ), + .valid_i ( req_i.w_valid ), + .ready_o ( w_b_fifo_ready ), + .data_o ( /* NOT CONNECTED */ ), + .valid_o ( w_b_fifo_valid ), + .ready_i ( req_i.b_ready ) + ); + assign rsp_o.b.resp = axi_pkg::RESP_OKAY; + assign rsp_o.b_valid = w_b_fifo_valid; + + // AR (ignored) + assign rsp_o.ar_ready = !w_ser_en_i; + + // R + axi_opt_lfsr #( + .Width ( DataWidth ) + ) i_axi_opt_lfsr_r ( + .clk_i, + .rst_ni, + .en_i ( r_lfsr_en ), + .ser_data_i ( r_ser_data_i ), + .ser_data_o ( r_ser_data_o ), + .ser_en_i ( r_ser_en_i ), + .inp_en_i ( 1'b0 ), + .data_i ( /* NOT CONNECTED */ ), + .data_o ( rsp_o.r.data ) + ); + assign rsp_o.r.resp = axi_pkg::RESP_OKAY; + assign r_lfsr_en = req_i.r_ready & rsp_o.r_valid; + assign rsp_o.r_valid = !r_ser_en_i; + +endmodule : axi_lite_lfsr + + +/// XOR LFSR with tabs based on the [lfsr_table](https://datacipy.cz/lfsr_table.pdf). LFSR has +/// a serial interface to set the initial state +module axi_opt_lfsr #( + parameter int unsigned Width = 32'd0 +) ( + /// Rising-edge clock + input logic clk_i, + /// Active-low reset + input logic rst_ni, + input logic en_i, + input logic ser_data_i, + output logic ser_data_o, + input logic ser_en_i, + input logic inp_en_i, + input logic [Width-1:0] data_i, + output logic [Width-1:0] data_o +); + + /// Number of bits required to hold the LFSR tab configuration + localparam int unsigned LfsrIdxWidth = cf_math_pkg::idx_width(Width); + /// Maximum number of tabs + localparam int unsigned MaxNumTabs = 4; + + /// Type specifying the tap positions + typedef logic [LfsrIdxWidth:0] xnor_entry_t [MaxNumTabs-1:0]; + xnor_entry_t XnorFeedback; + + // the shift register + logic [Width-1:0] reg_d, reg_q; + + // the feedback signal + logic xnor_feedback; + + always_comb begin : gen_register + + // get the parameters + case (Width) + 'd8 : XnorFeedback = { 'd8, 'd6, 'd5, 'd4 }; + 'd16 : XnorFeedback = { 'd16, 'd14, 'd13, 'd11 }; + 'd32 : XnorFeedback = { 'd32, 'd30, 'd26, 'd25 }; + 'd64 : XnorFeedback = { 'd64, 'd63, 'd61, 'd60 }; + 'd128 : XnorFeedback = { 'd128, 'd127, 'd126, 'd119 }; + 'd256 : XnorFeedback = { 'd256, 'd256, 'd521, 'd246 }; + 'd512 : XnorFeedback = { 'd512, 'd510, 'd507, 'd504 }; + 'd1024 : XnorFeedback = { 'd1024, 'd1015, 'd1002, 'd1001 }; + default : XnorFeedback = { 'x, 'x, 'x, 'x }; + endcase + + // shift register functionality + // compression mode + if (inp_en_i) begin + for (int unsigned i = 0; i < Width - 1; i++) begin : gen_comp_conection + reg_d[i] = reg_q[i+1] ^ data_i[i]; + end + // generation mode + end else begin + for (int unsigned i = 0; i < Width - 1; i++) begin : gen_gen_conection + reg_d[i] = reg_q[i+1]; + end + end + // serial access mode + if (ser_en_i) begin + // new head element + reg_d[Width-1] = ser_data_i; + // LFSR mode + end else begin + xnor_feedback = reg_q[XnorFeedback[MaxNumTabs-1]-1]; + for (int unsigned t = 0; t < MaxNumTabs - 1; t++) begin : gen_feedback_path + xnor_feedback = xnor_feedback; + if (XnorFeedback[t] != 0) begin + xnor_feedback = xnor_feedback ^ reg_q[XnorFeedback[t]-1]; + end + end + reg_d[Width-1] = inp_en_i ? xnor_feedback ^ data_i[Width-1] : xnor_feedback; + end + end + + // connect outputs + assign ser_data_o = reg_q[0]; + assign data_o = reg_q; + + // state + `FFL(reg_q, reg_d, en_i | ser_en_i, '1, clk_i, rst_ni) + +endmodule : axi_opt_lfsr diff --git a/src_files.yml b/src_files.yml index a5bb04f88..23ec58c3f 100644 --- a/src_files.yml +++ b/src_files.yml @@ -30,6 +30,7 @@ axi: - src/axi_join.sv - src/axi_lite_demux.sv - src/axi_lite_join.sv + - src/axi_lite_lfsr.sv - src/axi_lite_mailbox.sv - src/axi_lite_mux.sv - src/axi_lite_regs.sv @@ -45,6 +46,7 @@ axi: - src/axi_err_slv.sv - src/axi_dw_converter.sv - src/axi_id_serialize.sv + - src/axi_lfsr.sv - src/axi_multicut.sv - src/axi_to_axi_lite.sv - src/axi_to_mem_banked.sv