From d4c2b40eb65e52b3fccb0e7785766921adf6e3bd Mon Sep 17 00:00:00 2001 From: PMiranda Date: Wed, 30 Oct 2024 18:31:17 +0000 Subject: [PATCH] feat(nco, axis): fix NCO, AXIS IN, AXIS OUT - update iob_nco module with new clock in domain, fix CDC problems - NOTE: linux software is outdated - add iob_axistream in and out modules, fix CDC problems - NOTE: axistream in still needs better CDC for `axis_word_count` to `NWORDS_rd`. Maybe use gray counter --- .../hardware/modules/fifo2axis/fifo2axis.py | 2 + .../fifo2axis/hardware/src/fifo2axis.v | 11 +- .../hardware/src/iob_axistream_in.v | 302 +++++++++++++++ .../axistream_in/iob_axistream_in.py | 297 ++++++++++++++ .../axistream_in/software/linux/Readme.md | 15 + .../software/linux/drivers/driver.mk | 1 + .../linux/drivers/iob_axistream_in_main.c | 361 ++++++++++++++++++ .../software/linux/iob_axistream_in.dts | 29 ++ .../software/src/iob-axistream-in.c | 15 + .../software/src/iob-axistream-in.h | 5 + .../src/iob_axistream_in_swreg_pc_emul.c | 30 ++ .../hardware/src/iob_axistream_out.v | 222 +++++++++++ .../axistream_out/iob_axistream_out.py | 284 ++++++++++++++ .../axistream_out/software/linux/Readme.md | 15 + .../software/linux/drivers/driver.mk | 1 + .../linux/drivers/iob_axistream_out_main.c | 359 +++++++++++++++++ .../software/linux/iob_axistream_out.dts | 29 ++ .../software/src/iob-axistream-out.c | 15 + .../software/src/iob-axistream-out.h | 5 + .../src/iob_axistream_out_swreg_pc_emul.c | 28 ++ .../hardware/simulation/src/iob_nco_tb.v | 11 +- .../modules/iob_nco/hardware/src/iob_nco.v | 153 ++++++-- .../iob_nco/hardware/src/iob_nco_sync.v | 133 +++++++ .../LIB/hardware/modules/iob_nco/iob_nco.py | 55 ++- .../modules/iob_nco/software/src/iob-nco.c | 9 +- .../modules/iob_nco/software/src/iob-nco.h | 2 +- 26 files changed, 2332 insertions(+), 57 deletions(-) create mode 100644 submodules/LIB/hardware/modules/iob_axistream/axistream_in/hardware/src/iob_axistream_in.v create mode 100755 submodules/LIB/hardware/modules/iob_axistream/axistream_in/iob_axistream_in.py create mode 100644 submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/linux/Readme.md create mode 100644 submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/linux/drivers/driver.mk create mode 100644 submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/linux/drivers/iob_axistream_in_main.c create mode 100644 submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/linux/iob_axistream_in.dts create mode 100644 submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/src/iob-axistream-in.c create mode 100644 submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/src/iob-axistream-in.h create mode 100644 submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/src/iob_axistream_in_swreg_pc_emul.c create mode 100644 submodules/LIB/hardware/modules/iob_axistream/axistream_out/hardware/src/iob_axistream_out.v create mode 100755 submodules/LIB/hardware/modules/iob_axistream/axistream_out/iob_axistream_out.py create mode 100644 submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/linux/Readme.md create mode 100644 submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/linux/drivers/driver.mk create mode 100644 submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/linux/drivers/iob_axistream_out_main.c create mode 100644 submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/linux/iob_axistream_out.dts create mode 100644 submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/src/iob-axistream-out.c create mode 100644 submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/src/iob-axistream-out.h create mode 100644 submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/src/iob_axistream_out_swreg_pc_emul.c create mode 100644 submodules/LIB/hardware/modules/iob_nco/hardware/src/iob_nco_sync.v diff --git a/submodules/LIB/hardware/modules/fifo2axis/fifo2axis.py b/submodules/LIB/hardware/modules/fifo2axis/fifo2axis.py index 8eac37287..69ad55b7f 100644 --- a/submodules/LIB/hardware/modules/fifo2axis/fifo2axis.py +++ b/submodules/LIB/hardware/modules/fifo2axis/fifo2axis.py @@ -2,6 +2,7 @@ from iob_module import iob_module from iob_reg_re import iob_reg_re +from iob_modcnt import iob_modcnt class fifo2axis(iob_module): @@ -15,6 +16,7 @@ def _create_submodules_list(cls): super()._create_submodules_list( [ iob_reg_re, + iob_modcnt, {"interface": "clk_en_rst_s_port"}, {"interface": "clk_en_rst_s_s_portmap"}, ] diff --git a/submodules/LIB/hardware/modules/fifo2axis/hardware/src/fifo2axis.v b/submodules/LIB/hardware/modules/fifo2axis/hardware/src/fifo2axis.v index db13e7df9..3e7763a02 100644 --- a/submodules/LIB/hardware/modules/fifo2axis/hardware/src/fifo2axis.v +++ b/submodules/LIB/hardware/modules/fifo2axis/hardware/src/fifo2axis.v @@ -46,27 +46,30 @@ module fifo2axis #( //FIFO tlast wire axis_tlast_nxt; - assign axis_tlast_nxt = (axis_word_count == len_i); + wire [AXIS_LEN_W-1:0] len_int; + assign len_int = len_i - 1'b1; + assign axis_tlast_nxt = (axis_word_count == len_int); iob_reg_re #( .DATA_W (1), .RST_VAL(1'd0) ) axis_tlast_reg ( `include "clk_en_rst_s_s_portmap.vs" .rst_i (rst_i), - .en_i (en_i), + .en_i (pipe_en), .data_i(axis_tlast_nxt), .data_o(axis_tlast_o) ); //tdata word count - iob_counter #( + iob_modcnt #( .DATA_W (AXIS_LEN_W), - .RST_VAL(0) + .RST_VAL({AXIS_LEN_W{1'b1}}) // go to 0 after first enable ) word_count_inst ( `include "clk_en_rst_s_s_portmap.vs" .rst_i (rst_i), .en_i (fifo_read_o), + .mod_i (len_int), .data_o(axis_word_count) ); diff --git a/submodules/LIB/hardware/modules/iob_axistream/axistream_in/hardware/src/iob_axistream_in.v b/submodules/LIB/hardware/modules/iob_axistream/axistream_in/hardware/src/iob_axistream_in.v new file mode 100644 index 000000000..cba4fbcca --- /dev/null +++ b/submodules/LIB/hardware/modules/iob_axistream/axistream_in/hardware/src/iob_axistream_in.v @@ -0,0 +1,302 @@ +`timescale 1ns / 1ps +`include "iob_utils.vh" +`include "iob_axistream_in_conf.vh" +`include "iob_axistream_in_swreg_def.vh" + +module iob_axistream_in #( + `include "iob_axistream_in_params.vs" +) ( + `include "iob_axistream_in_io.vs" +); + + localparam R = DATA_W / TDATA_W; + localparam R_W = $clog2(R); + localparam RAM_ADDR_W = FIFO_ADDR_W - $clog2(R); + + //rst and enable synced to axis_clk + wire axis_sw_rst; + wire axis_sw_enable; + wire axis_sw_mode; + + //fifo write + wire axis_fifo_write; + wire axis_fifo_full; + + //tlast detected + wire axis_tlast; + wire axis_tlast_detected; + + //word counter + wire [ DATA_W-1:0] axis_word_count; + wire axis_word_count_en; + + + //fifo read + wire fifo_read; + wire [ DATA_W-1:0] fifo_data; + wire fifo_empty; + wire [((FIFO_ADDR_W+1 > 1) ? FIFO_ADDR_W+1 : 1)-1:0] fifo_level; + + //fifo RAM + wire ext_mem_w_clk; + wire [ R-1:0] ext_mem_w_en; + wire [RAM_ADDR_W-1:0] ext_mem_w_addr; + wire [ DATA_W-1:0] ext_mem_w_data; + wire ext_mem_r_clk; + wire [ R-1:0] ext_mem_r_en; + wire [RAM_ADDR_W-1:0] ext_mem_r_addr; + wire [ DATA_W-1:0] ext_mem_r_data; + + wire int_tvalid; + wire [DATA_W-1:0] int_tdata; + wire int_tready; + + // configuration control and status register file. + `include "iob_axistream_in_swreg_inst.vs" + + wire tlast_detected_reg; + + //CPU INTERFACE + assign DATA_rready_rd = int_tvalid; + assign interrupt_o = FIFO_LEVEL_rd >= FIFO_THRESHOLD_wr; + assign DATA_rvalid_rd = int_tvalid & (~MODE_wr); + assign DATA_rdata_rd = int_tdata; + + //System Stream output interface + // System output valid only if in system stream mode + assign sys_tvalid_o = int_tvalid & MODE_wr; + assign sys_tdata_o = int_tdata; + + assign int_tready = (MODE_wr) ? sys_tready_i : DATA_ren_rd; + + // empty = fifo empty + no data in fifo2axis + assign FIFO_EMPTY_rd = fifo_empty & (~int_tvalid); + // level = fifo level + data in fifo2axis + assign FIFO_LEVEL_rd = fifo_level + int_tvalid; + + wire ready_int; + // Ready if not full and, if in CSR mode, tlast not detected + assign ready_int = ~axis_fifo_full & axis_sw_enable & ~(~axis_sw_mode & tlast_detected_reg); + + //word count enable + assign axis_word_count_en = axis_fifo_write & ~tlast_detected_reg; + + generate + if (R == 1) begin : gen_no_padding + //AXI Stream input interface + assign axis_tready_o = ready_int; + //FIFO write + assign axis_fifo_write = axis_tvalid_i & axis_tready_o; + end else begin : gen_padding + //FIFO write FSM + reg fifo_write_state_nxt; + wire fifo_write_state; + always @* begin + fifo_write_state_nxt = fifo_write_state; + case (fifo_write_state) + 0: begin // Idle + // If tvalid, fifo not full, tlast, and the write wont fill the DATA_W with TDATA_W + if (((axis_tvalid_i & ~axis_fifo_full) & axis_tlast_i) & + axis_word_count[0+:R_W] != {R_W{1'd1}}) begin + fifo_write_state_nxt = 1'b1; + end + end + default: begin // Padding + if (axis_word_count[0+:R_W] == {R_W{1'd1}} && ~axis_fifo_full) begin + fifo_write_state_nxt = 1'b0; + end + end + endcase + end + + iob_reg_re #( + .DATA_W (1), + .RST_VAL(1'd0) + ) fifo_write_state_reg ( + .clk_i (axis_clk_i), + .cke_i (axis_cke_i), + .arst_i(axis_arst_i), + .rst_i (axis_sw_rst), + .en_i (axis_sw_enable), + .data_i(fifo_write_state_nxt), + .data_o(fifo_write_state) + ); + + // Ready if not full, if in CSR mode, tlast not detected and not in padding state + assign axis_tready_o = ready_int & ~fifo_write_state; + //FIFO write if tvalid, tlast, not full or in padding state + assign axis_fifo_write = (axis_tvalid_i & axis_tready_o) | fifo_write_state; + end + endgenerate + + //tlast + assign axis_tlast = axis_tlast_i & axis_fifo_write; + + // received words counter + iob_counter #( + .DATA_W (DATA_W), + .RST_VAL(0) + ) word_count_inst ( + .clk_i (axis_clk_i), + .cke_i (axis_cke_i), + .arst_i(axis_arst_i), + .rst_i (axis_sw_rst), + .en_i (axis_word_count_en), + .data_o(axis_word_count) + ); + + + //Synchronizers from clk (swregs) to axis domain + iob_sync #( + .DATA_W (1), + .RST_VAL(1'd0) + ) sw_rst ( + .clk_i (axis_clk_i), + .arst_i (axis_arst_i), + .signal_i(SOFT_RESET_wr), + .signal_o(axis_sw_rst) + ); + + iob_sync #( + .DATA_W (1), + .RST_VAL(1'd0) + ) sw_enable ( + .clk_i (axis_clk_i), + .arst_i (axis_arst_i), + .signal_i(ENABLE_wr), + .signal_o(axis_sw_enable) + ); + + iob_sync #( + .DATA_W (1), + .RST_VAL(1'd0) + ) sw_mode ( + .clk_i (axis_clk_i), + .arst_i (axis_arst_i), + .signal_i(MODE_wr), + .signal_o(axis_sw_mode) + ); + + + //Synchronizers from axis to clk domain (sw_regs) + iob_sync #( + .DATA_W (1), + .RST_VAL(1'd0) + ) tlast_detected_sync ( + .clk_i (clk_i), + .arst_i (arst_i), + .signal_i(tlast_detected_reg), + .signal_o(TLAST_DETECTED_rd) + ); + + iob_sync #( + .DATA_W (DATA_W), + .RST_VAL(0) + ) word_counter_sync ( + .clk_i (clk_i), + .arst_i (arst_i), + .signal_i(axis_word_count), + .signal_o(NWORDS_rd) + ); + + //tlast detection + iob_edge_detect #( + .EDGE_TYPE("rising"), + .OUT_TYPE ("step") + ) tlast_detect ( + .clk_i (axis_clk_i), + .cke_i (axis_cke_i), + .arst_i (axis_arst_i), + .rst_i (axis_sw_rst), + .bit_i (axis_tlast), + .detected_o(axis_tlast_detected) + ); + + iob_reg #( + .DATA_W (1), + .RST_VAL(1'd0) + ) tlast_detect_reg ( + .clk_i (axis_clk_i), + .cke_i (axis_cke_i), + .arst_i(axis_arst_i), + .data_i(axis_tlast_detected), + .data_o(tlast_detected_reg) + ); + + //FIFOs RAM + genvar p; + generate + for (p = 0; p < R; p = p + 1) begin : gen_fifo_ram + iob_ram_t2p #( + .DATA_W(TDATA_W), + .ADDR_W(RAM_ADDR_W) + ) iob_ram_t2p ( + .w_clk_i (ext_mem_w_clk), + .w_en_i (ext_mem_w_en[p]), + .w_addr_i(ext_mem_w_addr), + .w_data_i(ext_mem_w_data[p*TDATA_W+:TDATA_W]), + + .r_clk_i (ext_mem_r_clk), + .r_en_i (ext_mem_r_en[p]), + .r_addr_i(ext_mem_r_addr), + .r_data_o(ext_mem_r_data[p*TDATA_W+:TDATA_W]) + ); + end + endgenerate + + fifo2axis #( + .DATA_W(DATA_W), + .AXIS_LEN_W(1) + ) fifo2axis_inst ( + `include "clk_en_rst_s_s_portmap.vs" + .rst_i(SOFT_RESET_wr), + .en_i(1'b1), + .len_i(1'b1), + // FIFO I/F + .fifo_empty_i(fifo_empty), + .fifo_read_o(fifo_read), + .fifo_rdata_i(fifo_data), + // AXIS I/F + .axis_tvalid_o(int_tvalid), + .axis_tdata_o(int_tdata), + .axis_tready_i(int_tready), + .axis_tlast_o() + ); + + //async fifo + iob_fifo_async #( + .W_DATA_W(TDATA_W), + .R_DATA_W(DATA_W), + .ADDR_W (FIFO_ADDR_W) + ) data_fifo ( + .ext_mem_w_clk_o (ext_mem_w_clk), + .ext_mem_w_en_o (ext_mem_w_en), + .ext_mem_w_addr_o(ext_mem_w_addr), + .ext_mem_w_data_o(ext_mem_w_data), + .ext_mem_r_clk_o (ext_mem_r_clk), + .ext_mem_r_en_o (ext_mem_r_en), + .ext_mem_r_addr_o(ext_mem_r_addr), + .ext_mem_r_data_i(ext_mem_r_data), + //read port (sys clk domain) + .r_clk_i (clk_i), + .r_cke_i (cke_i), + .r_arst_i (arst_i), + .r_rst_i (SOFT_RESET_wr), + .r_en_i (fifo_read), + .r_data_o (fifo_data), + .r_empty_o (fifo_empty), + .r_full_o (FIFO_FULL_rd), + .r_level_o (fifo_level), + //write port (axis clk domain) + .w_clk_i (axis_clk_i), + .w_cke_i (axis_cke_i), + .w_arst_i (axis_arst_i), + .w_rst_i (axis_sw_rst), + .w_en_i (axis_fifo_write), + .w_data_i (axis_tdata_i), + .w_empty_o (), + .w_full_o (axis_fifo_full), + .w_level_o () + ); + +endmodule diff --git a/submodules/LIB/hardware/modules/iob_axistream/axistream_in/iob_axistream_in.py b/submodules/LIB/hardware/modules/iob_axistream/axistream_in/iob_axistream_in.py new file mode 100755 index 000000000..8ea88b0b6 --- /dev/null +++ b/submodules/LIB/hardware/modules/iob_axistream/axistream_in/iob_axistream_in.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python3 + +import os + +from iob_module import iob_module + +# Submodules +from iob_reg_re import iob_reg_re +from iob_ram_t2p import iob_ram_t2p +from iob_fifo_async import iob_fifo_async +from iob_sync import iob_sync +from iob_counter import iob_counter +from iob_edge_detect import iob_edge_detect +from fifo2axis import fifo2axis + + +class iob_axistream_in(iob_module): + name = "iob_axistream_in" + version = "V0.30" + setup_dir = os.path.dirname(__file__) + + @classmethod + def _create_submodules_list(cls): + """Create submodules list with dependencies of this module""" + super()._create_submodules_list( + [ + {"interface": "iob_s_port"}, + {"interface": "iob_s_portmap"}, + iob_fifo_async, + iob_reg_re, + iob_ram_t2p, + iob_sync, + iob_counter, + iob_edge_detect, + fifo2axis, + ] + ) + + @classmethod + def _setup_confs(cls): + super()._setup_confs( + [ + # Macros + # Parameters + { + "name": "DATA_W", + "type": "P", + "val": "32", + "min": "32", + "max": "32", + "descr": "CPU data bus width", + }, + { + "name": "ADDR_W", + "type": "P", + "val": "`IOB_AXISTREAM_IN_SWREG_ADDR_W", + "min": "NA", + "max": "NA", + "descr": "Address bus width", + }, + { + "name": "TDATA_W", + "type": "P", + "val": "8", + "min": "1", + "max": "DATA_W", + "descr": "AXI stream data width", + }, + { + "name": "FIFO_ADDR_W", + "type": "P", + "val": "4", + "min": "NA", + "max": "16", + "descr": "FIFO depth (log2)", + }, + ] + ) + + @classmethod + def _setup_ios(cls): + cls.ios += [ + {"name": "iob_s_port", "descr": "CPU native interface", "ports": []}, + { + "name": "general", + "descr": "System general interface signals", + "ports": [ + { + "name": "clk_i", + "type": "I", + "n_bits": "1", + "descr": "System clock input", + }, + { + "name": "arst_i", + "type": "I", + "n_bits": "1", + "descr": "System reset, asynchronous and active high", + }, + { + "name": "cke_i", + "type": "I", + "n_bits": "1", + "descr": "System clock enable signal.", + }, + { + "name": "interrupt_o", + "type": "O", + "n_bits": "1", + "descr": "FIFO threshold interrupt signal", + }, + ], + }, + { + "name": "axistream", + "descr": "AXI Stream interface signals", + "ports": [ + { + "name": "axis_clk_i", + "type": "I", + "n_bits": "1", + "descr": "Clock.", + }, + { + "name": "axis_cke_i", + "type": "I", + "n_bits": "1", + "descr": "Clock enable", + }, + { + "name": "axis_arst_i", + "type": "I", + "n_bits": "1", + "descr": "Asynchronous and active high reset.", + }, + { + "name": "axis_tdata_i", + "type": "I", + "n_bits": "TDATA_W", + "descr": "Data.", + }, + { + "name": "axis_tvalid_i", + "type": "I", + "n_bits": "1", + "descr": "Valid.", + }, + { + "name": "axis_tready_o", + "type": "O", + "n_bits": "1", + "descr": "Ready.", + }, + { + "name": "axis_tlast_i", + "type": "I", + "n_bits": "1", + "descr": "Last word.", + }, + ], + }, + { + "name": "sys_axis", + "descr": "System AXI Stream interface.", + "ports": [ + { + "name": "sys_tdata_o", + "type": "O", + "n_bits": "DATA_W", + "descr": "Data.", + }, + { + "name": "sys_tvalid_o", + "type": "O", + "n_bits": "1", + "descr": "Valid.", + }, + { + "name": "sys_tready_i", + "type": "I", + "n_bits": "1", + "descr": "Ready.", + }, + ], + }, + ] + + @classmethod + def _setup_regs(cls): + cls.regs += [ + { + "name": "axistream", + "descr": "AXI Stream software accessible registers.", + "regs": [ + { + "name": "SOFT_RESET", + "type": "W", + "n_bits": 1, + "rst_val": 0, + "log2n_items": 0, + "autoreg": True, + "descr": "Soft reset.", + }, + { + "name": "ENABLE", + "type": "W", + "n_bits": 1, + "rst_val": 0, + "log2n_items": 0, + "autoreg": True, + "descr": "Enable peripheral.", + }, + { + "name": "DATA", + "type": "R", + "n_bits": 32, + "rst_val": 0, + "log2n_items": 0, + "autoreg": False, + "descr": "Data output.", + }, + { + "name": "MODE", + "type": "W", + "n_bits": "1", + "rst_val": 0, + "log2n_items": 0, + "autoreg": True, + "descr": "Sets the operation mode: (0) data is read using CSR; (1) data is read using system axistream interface.", + }, + { + "name": "NWORDS", + "type": "R", + "n_bits": "DATA_W", + "rst_val": 0, + "log2n_items": 0, + "autoreg": True, + "descr": "Read the number of words (with TDATA_W bits) written to the FIFO.", + }, + { + "name": "TLAST_DETECTED", + "type": "R", + "n_bits": 1, + "rst_val": 0, + "log2n_items": 0, + "autoreg": True, + "descr": "Read the TLAST detected status.", + }, + ], + }, + { + "name": "fifo", + "descr": "FIFO related registers", + "regs": [ + { + "name": "FIFO_FULL", + "type": "R", + "n_bits": 1, + "rst_val": 0, + "log2n_items": 0, + "autoreg": True, + "descr": "Full (1), or non-full (0).", + }, + { + "name": "FIFO_EMPTY", + "type": "R", + "n_bits": 1, + "rst_val": 0, + "log2n_items": 0, + "autoreg": True, + "descr": "Full (1), or non-full (0).", + }, + { + "name": "FIFO_THRESHOLD", + "type": "W", + "n_bits": "FIFO_ADDR_W+1", + "rst_val": 8, + "log2n_items": 0, + "autoreg": True, + "descr": "FIFO threshold level for interrupt signal", + }, + { + "name": "FIFO_LEVEL", + "type": "R", + "n_bits": "FIFO_ADDR_W+1", + "rst_val": 0, + "log2n_items": 0, + "autoreg": True, + "descr": "Current FIFO level", + }, + ], + }, + ] + + @classmethod + def _setup_block_groups(cls): + cls.block_groups += [] diff --git a/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/linux/Readme.md b/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/linux/Readme.md new file mode 100644 index 000000000..84daacdd6 --- /dev/null +++ b/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/linux/Readme.md @@ -0,0 +1,15 @@ +# IOb AXIStream In Linux Kernel Drivers +- Structure: + - `drivers/`: directory with linux kernel module drivers for + iob_axistream_in + - `iob_axistream_in_main.c`: driver source + - `[iob_axistream_in.h]` and `[iob_axistream_in_sysfs.h]`: header files + generated by: + ```bash + python3 .path/to/iob-linux/scripts/drivers.py iob_axistream_in -o [output_dir] + ``` + - `driver.mk`: makefile segment with `iob_axistream_in-obj:` target for driver + compilation + - `iob_axistream_in.dts`: device tree template with iob_axistream_in node + - manually add the `axistream_in` node to the system device tree so the + iob_axistream_in is recognized by the linux kernel diff --git a/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/linux/drivers/driver.mk b/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/linux/drivers/driver.mk new file mode 100644 index 000000000..a95d9097c --- /dev/null +++ b/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/linux/drivers/driver.mk @@ -0,0 +1 @@ +iob_axistream_in-objs := iob_axistream_in_main.o iob_class/iob_class_utils.o diff --git a/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/linux/drivers/iob_axistream_in_main.c b/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/linux/drivers/iob_axistream_in_main.c new file mode 100644 index 000000000..c480177f8 --- /dev/null +++ b/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/linux/drivers/iob_axistream_in_main.c @@ -0,0 +1,361 @@ +/* iob_axistream_in_main.c: driver for iob_axistream_in + * using device platform. No hardcoded hardware address: + * 1. load driver: insmod iob_axistream_in.ko + * 2. run user app: ./user/user + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iob_axistream_in.h" +#include "iob_class/iob_class_utils.h" + +static int iob_axistream_in_probe(struct platform_device *); +static int iob_axistream_in_remove(struct platform_device *); + +static ssize_t iob_axistream_in_read(struct file *, char __user *, size_t, + loff_t *); +static ssize_t iob_axistream_in_write(struct file *, const char __user *, + size_t, loff_t *); +static loff_t iob_axistream_in_llseek(struct file *, loff_t, int); +static int iob_axistream_in_open(struct inode *, struct file *); +static int iob_axistream_in_release(struct inode *, struct file *); + +static struct iob_data iob_axistream_in_data = {0}; +DEFINE_MUTEX(iob_axistream_in_mutex); + +#include "iob_axistream_in_sysfs.h" + +static const struct file_operations iob_axistream_in_fops = { + .owner = THIS_MODULE, + .write = iob_axistream_in_write, + .read = iob_axistream_in_read, + .llseek = iob_axistream_in_llseek, + .open = iob_axistream_in_open, + .release = iob_axistream_in_release, +}; + +static const struct of_device_id of_iob_axistream_in_match[] = { + {.compatible = "iobundle,axistream_in0"}, + {}, +}; + +static struct platform_driver iob_axistream_in_driver = { + .driver = + { + .name = "iob_axistream_in", + .owner = THIS_MODULE, + .of_match_table = of_iob_axistream_in_match, + }, + .probe = iob_axistream_in_probe, + .remove = iob_axistream_in_remove, +}; + +// +// Module init and exit functions +// +static int iob_axistream_in_probe(struct platform_device *pdev) { + struct resource *res; + int result = 0; + + if (iob_axistream_in_data.device != NULL) { + pr_err("[Driver] %s: No more devices allowed!\n", + IOB_AXISTREAM_IN_DRIVER_NAME); + + return -ENODEV; + } + + pr_info("[Driver] %s: probing.\n", IOB_AXISTREAM_IN_DRIVER_NAME); + + // Get the I/O region base address + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + pr_err("[Driver]: Failed to get I/O resource!\n"); + result = -ENODEV; + goto r_get_resource; + } + + // Request and map the I/O region + iob_axistream_in_data.regbase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(iob_axistream_in_data.regbase)) { + result = PTR_ERR(iob_axistream_in_data.regbase); + goto r_ioremmap; + } + iob_axistream_in_data.regsize = resource_size(res); + + // Alocate char device + result = alloc_chrdev_region(&iob_axistream_in_data.devnum, 0, 1, + IOB_AXISTREAM_IN_DRIVER_NAME); + if (result) { + pr_err("%s: Failed to allocate device number!\n", + IOB_AXISTREAM_IN_DRIVER_NAME); + goto r_alloc_region; + } + + cdev_init(&iob_axistream_in_data.cdev, &iob_axistream_in_fops); + + result = + cdev_add(&iob_axistream_in_data.cdev, iob_axistream_in_data.devnum, 1); + if (result) { + pr_err("%s: Char device registration failed!\n", + IOB_AXISTREAM_IN_DRIVER_NAME); + goto r_cdev_add; + } + + // Create device class // todo: make a dummy driver just to create and own the + // class: https://stackoverflow.com/a/16365027/8228163 + if ((iob_axistream_in_data.class = + class_create(THIS_MODULE, IOB_AXISTREAM_IN_DRIVER_CLASS)) == NULL) { + printk("Device class can not be created!\n"); + goto r_class; + } + + // Create device file + iob_axistream_in_data.device = device_create( + iob_axistream_in_data.class, NULL, iob_axistream_in_data.devnum, NULL, + IOB_AXISTREAM_IN_DRIVER_NAME); + if (iob_axistream_in_data.device == NULL) { + printk("Can not create device file!\n"); + goto r_device; + } + + result = + iob_axistream_in_create_device_attr_files(iob_axistream_in_data.device); + if (result) { + pr_err("Cannot create device attribute file......\n"); + goto r_dev_file; + } + + dev_info(&pdev->dev, "initialized.\n"); + goto r_ok; + +r_dev_file: + iob_axistream_in_remove_device_attr_files(&iob_axistream_in_data); +r_device: + class_destroy(iob_axistream_in_data.class); +r_class: + cdev_del(&iob_axistream_in_data.cdev); +r_cdev_add: + unregister_chrdev_region(iob_axistream_in_data.devnum, 1); +r_alloc_region: + // iounmap is managed by devm +r_ioremmap: +r_get_resource: +r_ok: + + return result; +} + +static int iob_axistream_in_remove(struct platform_device *pdev) { + iob_axistream_in_remove_device_attr_files(&iob_axistream_in_data); + class_destroy(iob_axistream_in_data.class); + cdev_del(&iob_axistream_in_data.cdev); + unregister_chrdev_region(iob_axistream_in_data.devnum, 1); + // Note: no need for iounmap, since we are using devm_ioremap_resource() + + dev_info(&pdev->dev, "exiting.\n"); + + return 0; +} + +static int __init iob_axistream_in_init(void) { + pr_info("[Driver] %s: initializing.\n", IOB_AXISTREAM_IN_DRIVER_NAME); + + return platform_driver_register(&iob_axistream_in_driver); +} + +static void __exit iob_axistream_in_exit(void) { + pr_info("[Driver] %s: exiting.\n", IOB_AXISTREAM_IN_DRIVER_NAME); + platform_driver_unregister(&iob_axistream_in_driver); +} + +// +// File operations +// + +static int iob_axistream_in_open(struct inode *inode, struct file *file) { + pr_info("[Driver] iob_axistream_in device opened\n"); + + if (!mutex_trylock(&iob_axistream_in_mutex)) { + pr_info("Another process is accessing the device\n"); + + return -EBUSY; + } + + return 0; +} + +static int iob_axistream_in_release(struct inode *inode, struct file *file) { + pr_info("[Driver] iob_axistream_in device closed\n"); + + mutex_unlock(&iob_axistream_in_mutex); + + return 0; +} + +static ssize_t iob_axistream_in_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) { + int size = 0; + u32 value = 0; + + /* read value from register */ + switch (*ppos) { + case IOB_AXISTREAM_IN_DATA_ADDR: + value = + iob_data_read_reg(iob_axistream_in_data.regbase, + IOB_AXISTREAM_IN_DATA_ADDR, IOB_AXISTREAM_IN_DATA_W); + size = (IOB_AXISTREAM_IN_DATA_W >> 3); // bit to bytes + pr_info("[Driver] Read DATA!\n"); + break; + case IOB_AXISTREAM_IN_NWORDS_ADDR: + value = iob_data_read_reg(iob_axistream_in_data.regbase, + IOB_AXISTREAM_IN_NWORDS_ADDR, + IOB_AXISTREAM_IN_NWORDS_W); + size = (IOB_AXISTREAM_IN_NWORDS_W >> 3); // bit to bytes + pr_info("[Driver] Read NWORDS!\n"); + break; + case IOB_AXISTREAM_IN_TLAST_DETECTED_ADDR: + value = iob_data_read_reg(iob_axistream_in_data.regbase, + IOB_AXISTREAM_IN_TLAST_DETECTED_ADDR, + IOB_AXISTREAM_IN_TLAST_DETECTED_W); + size = (IOB_AXISTREAM_IN_TLAST_DETECTED_W >> 3); // bit to bytes + pr_info("[Driver] Read TLAST_DETECTED!\n"); + break; + case IOB_AXISTREAM_IN_FIFO_FULL_ADDR: + value = iob_data_read_reg(iob_axistream_in_data.regbase, + IOB_AXISTREAM_IN_FIFO_FULL_ADDR, + IOB_AXISTREAM_IN_FIFO_FULL_W); + size = (IOB_AXISTREAM_IN_FIFO_FULL_W >> 3); // bit to bytes + pr_info("[Driver] Read FIFO_FULL!\n"); + break; + case IOB_AXISTREAM_IN_FIFO_EMPTY_ADDR: + value = iob_data_read_reg(iob_axistream_in_data.regbase, + IOB_AXISTREAM_IN_FIFO_EMPTY_ADDR, + IOB_AXISTREAM_IN_FIFO_EMPTY_W); + size = (IOB_AXISTREAM_IN_FIFO_EMPTY_W >> 3); // bit to bytes + pr_info("[Driver] Read FIFO_EMPTY!\n"); + break; + case IOB_AXISTREAM_IN_FIFO_LEVEL_ADDR: + value = iob_data_read_reg(iob_axistream_in_data.regbase, + IOB_AXISTREAM_IN_FIFO_LEVEL_ADDR, + IOB_AXISTREAM_IN_FIFO_LEVEL_W); + size = (IOB_AXISTREAM_IN_FIFO_LEVEL_W >> 3); // bit to bytes + pr_info("[Driver] Read FIFO_LEVEL!\n"); + break; + case IOB_AXISTREAM_IN_VERSION_ADDR: + value = iob_data_read_reg(iob_axistream_in_data.regbase, + IOB_AXISTREAM_IN_VERSION_ADDR, + IOB_AXISTREAM_IN_VERSION_W); + size = (IOB_AXISTREAM_IN_VERSION_W >> 3); // bit to bytes + pr_info("[Driver] Read version!\n"); + break; + default: + // invalid address - no bytes read + return 0; + } + + // Read min between count and REG_SIZE + if (size > count) + size = count; + + if (copy_to_user(buf, &value, size)) + return -EFAULT; + + return count; +} + +static ssize_t iob_axistream_in_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) { + int size = 0; + u32 value = 0; + + switch (*ppos) { + case IOB_AXISTREAM_IN_SOFT_RESET_ADDR: + size = (IOB_AXISTREAM_IN_SOFT_RESET_W >> 3); // bit to bytes + if (read_user_data(buf, size, &value)) + return -EFAULT; + iob_data_write_reg(iob_axistream_in_data.regbase, value, + IOB_AXISTREAM_IN_SOFT_RESET_ADDR, + IOB_AXISTREAM_IN_SOFT_RESET_W); + pr_info("[Driver] SOFT_RESET iob_axistream_in: 0x%x\n", value); + break; + case IOB_AXISTREAM_IN_ENABLE_ADDR: + size = (IOB_AXISTREAM_IN_ENABLE_W >> 3); // bit to bytes + if (read_user_data(buf, size, &value)) + return -EFAULT; + iob_data_write_reg(iob_axistream_in_data.regbase, value, + IOB_AXISTREAM_IN_ENABLE_ADDR, IOB_AXISTREAM_IN_ENABLE_W); + pr_info("[Driver] ENABLE iob_axistream_in: 0x%x\n", value); + break; + case IOB_AXISTREAM_IN_MODE_ADDR: + size = (IOB_AXISTREAM_IN_MODE_W >> 3); // bit to bytes + if (read_user_data(buf, size, &value)) + return -EFAULT; + iob_data_write_reg(iob_axistream_in_data.regbase, value, + IOB_AXISTREAM_IN_MODE_ADDR, IOB_AXISTREAM_IN_MODE_W); + pr_info("[Driver] MODE iob_axistream_in: 0x%x\n", value); + break; + case IOB_AXISTREAM_IN_FIFO_THRESHOLD_ADDR: + size = (IOB_AXISTREAM_IN_FIFO_THRESHOLD_W >> 3); // bit to bytes + if (read_user_data(buf, size, &value)) + return -EFAULT; + iob_data_write_reg(iob_axistream_in_data.regbase, value, + IOB_AXISTREAM_IN_FIFO_THRESHOLD_ADDR, + IOB_AXISTREAM_IN_FIFO_THRESHOLD_W); + pr_info("[Driver] FIFO_THRESHOLD iob_axistream_in: 0x%x\n", value); + break; + default: + pr_info("[Driver] Invalid write address 0x%x\n", (unsigned int)*ppos); + // invalid address - no bytes written + return 0; + } + + return count; +} + +/* Custom lseek function + * check: lseek(2) man page for whence modes + */ +static loff_t iob_axistream_in_llseek(struct file *filp, loff_t offset, + int whence) { + loff_t new_pos = -1; + + switch (whence) { + case SEEK_SET: + new_pos = offset; + break; + case SEEK_CUR: + new_pos = filp->f_pos + offset; + break; + case SEEK_END: + new_pos = (1 << IOB_AXISTREAM_IN_SWREG_ADDR_W) + offset; + break; + default: + return -EINVAL; + } + + // Check for valid bounds + if (new_pos < 0 || new_pos > iob_axistream_in_data.regsize) { + return -EINVAL; + } + + // Update file position + filp->f_pos = new_pos; + + return new_pos; +} + +module_init(iob_axistream_in_init); +module_exit(iob_axistream_in_exit); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_AUTHOR("IObundle"); +MODULE_DESCRIPTION("IOb-AXISTREAM-IN Drivers"); +MODULE_VERSION("0.10"); diff --git a/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/linux/iob_axistream_in.dts b/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/linux/iob_axistream_in.dts new file mode 100644 index 000000000..697148ee5 --- /dev/null +++ b/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/linux/iob_axistream_in.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* Copyright (c) 2024 IObundle */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + model = "IOb-SoC, VexRiscv"; + compatible = "IOb-SoC, VexRiscv"; + // CPU + // Memory + // Choosen + soc { + #address-cells = <1>; + #size-cells = <1>; + compatible = "iobundle,iob-soc", "simple-bus"; + ranges; + + // Other SOC peripherals go here + + // Add this Node to the device tree + AXISTREAMIN0: axistream_in@/*AXISTREAMIN0_ADDR_MACRO*/ { + compatible = "iobundle,axistream_in0"; + reg = <0x/*AXISTREAMIN0_ADDR_MACRO*/ 0x20>; + }; + + }; +}; diff --git a/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/src/iob-axistream-in.c b/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/src/iob-axistream-in.c new file mode 100644 index 000000000..9a4994c98 --- /dev/null +++ b/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/src/iob-axistream-in.c @@ -0,0 +1,15 @@ +#include "iob-axistream-in.h" + +void iob_axis_in_reset() { + IOB_AXISTREAM_IN_SET_SOFT_RESET(1); + IOB_AXISTREAM_IN_SET_SOFT_RESET(0); +} + +uint32_t iob_axis_read(uint32_t *value) { + if (IOB_AXISTREAM_IN_GET_FIFO_EMPTY()) { + return 0; + } else { + *value = IOB_AXISTREAM_IN_GET_DATA(); + return 1; + } +} diff --git a/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/src/iob-axistream-in.h b/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/src/iob-axistream-in.h new file mode 100644 index 000000000..3ffe9d72e --- /dev/null +++ b/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/src/iob-axistream-in.h @@ -0,0 +1,5 @@ +#include "iob_axistream_in_swreg.h" + +void iob_axis_in_reset(); + +uint32_t iob_axis_read(uint32_t *value); diff --git a/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/src/iob_axistream_in_swreg_pc_emul.c b/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/src/iob_axistream_in_swreg_pc_emul.c new file mode 100644 index 000000000..7bec1cf9e --- /dev/null +++ b/submodules/LIB/hardware/modules/iob_axistream/axistream_in/software/src/iob_axistream_in_swreg_pc_emul.c @@ -0,0 +1,30 @@ +/* PC Emulation of axistream-in peripheral */ + +#include + +#include "iob_axistream_in_swreg.h" + +// Base Address +static int base; +void IOB_AXISTREAM_IN_INIT_BASEADDR(uint32_t addr) { base = addr; } + +// Core Setters and Getters +uint32_t IOB_AXISTREAM_IN_GET_DATA() { return 0x00; } + +uint8_t IOB_AXISTREAM_IN_GET_EMPTY() { return 0x01; } + +uint8_t IOB_AXISTREAM_IN_GET_TLAST_DETECTED() { return 0x00; } + +uint32_t IOB_AXISTREAM_IN_GET_NWORDS() { return 0x00; } + +void IOB_AXISTREAM_IN_SET_SOFT_RESET(uint8_t value) {} + +void IOB_AXISTREAM_IN_SET_ENABLE(uint8_t value) {} + +void IOB_AXISTREAM_IN_SET_FIFO_THRESHOLD(uint32_t value) {} + +void IOB_AXISTREAM_IN_SET_MODE(uint8_t value) {} + +uint32_t IOB_AXISTREAM_IN_GET_FIFO_LEVEL() { return 0x00; } + +uint16_t IOB_AXISTREAM_IN_GET_VERSION() { return 0xaaaa; } diff --git a/submodules/LIB/hardware/modules/iob_axistream/axistream_out/hardware/src/iob_axistream_out.v b/submodules/LIB/hardware/modules/iob_axistream/axistream_out/hardware/src/iob_axistream_out.v new file mode 100644 index 000000000..b61ba3c40 --- /dev/null +++ b/submodules/LIB/hardware/modules/iob_axistream/axistream_out/hardware/src/iob_axistream_out.v @@ -0,0 +1,222 @@ +`timescale 1ns / 1ps +`include "iob_utils.vh" +`include "iob_axistream_out_conf.vh" +`include "iob_axistream_out_swreg_def.vh" + +module iob_axistream_out #( + `include "iob_axistream_out_params.vs" +) ( + `include "iob_axistream_out_io.vs" +); + + localparam R = DATA_W / TDATA_W; + localparam RAM_ADDR_W = FIFO_ADDR_W - $clog2(R); + + //rst and enble synced to axis_clk + wire axis_sw_rst; + wire axis_sw_enable; + + //fifo write + wire fifo_write; + wire [ DATA_W-1:0] fifo_wdata; + + //fifo read + wire axis_fifo_empty; + reg axis_fifo_read; + wire axis_pc; + reg axis_pc_nxt; + wire [ TDATA_W-1:0] axis_tdata; + reg axis_tvalid; + + //word counter + wire [ DATA_W-1:0] axis_word_count; + wire [ DATA_W-1:0] axis_nwords; + + //fifo ram + wire ext_mem_w_clk; + wire [ R-1:0] ext_mem_w_en; + wire [RAM_ADDR_W-1:0] ext_mem_w_addr; + wire [ DATA_W-1:0] ext_mem_w_data; + wire ext_mem_r_clk; + wire [ R-1:0] ext_mem_r_en; + wire [RAM_ADDR_W-1:0] ext_mem_r_addr; + wire [ DATA_W-1:0] ext_mem_r_data; + + // configuration control and status register file. + `include "iob_axistream_out_swreg_inst.vs" + + //AXI Stream interface + assign axis_tvalid_o = axis_tvalid; + assign axis_tdata_o = axis_tdata; + assign axis_tlast_o = (axis_word_count == axis_nwords) & axis_tvalid_o; + + //CPU interface + assign DATA_wready_wr = ~FIFO_FULL_rd; + assign interrupt_o = FIFO_LEVEL_rd <= FIFO_THRESHOLD_wr; + + //DMA data ready + assign sys_tready_o = ~FIFO_FULL_rd & ENABLE_wr & (MODE_wr == 1'b1); + + //FIFO write + assign fifo_write = ((DATA_wen_wr & (MODE_wr == 1'b0)) | + (sys_tvalid_i & (MODE_wr == 1'b1))) & + ENABLE_wr; + assign fifo_wdata = (sys_tvalid_i == 1'b1) ? sys_tdata_i : DATA_wdata_wr; + + //FIFO read + always @* begin + axis_pc_nxt = axis_pc + 1'b1; + axis_fifo_read = 1'b0; + axis_tvalid = 1'b0; + + case (axis_pc) + 0: begin + if (axis_fifo_empty) begin + axis_pc_nxt = axis_pc; + end else begin + axis_fifo_read = 1'b1; + end + end + default: begin + if (axis_word_count <= axis_nwords) begin // Not in padding + axis_tvalid = axis_sw_enable; + axis_pc_nxt = axis_pc; + if (axis_tready_i && axis_sw_enable) begin + if (axis_fifo_empty) begin + axis_pc_nxt = 1'b0; + end else begin + axis_fifo_read = 1'b1; + end + end + end else begin // In padding bytes (read them whithout tvalid and not waiting for tready) + if (axis_fifo_empty) begin + axis_pc_nxt = 1'b0; + end else begin + axis_pc_nxt = axis_pc; + axis_fifo_read = 1'b1; + end + end + end + endcase + end + + // program counter + iob_reg_re #( + .DATA_W (1), + .RST_VAL(1'd0) + ) tvalid_reg ( + .clk_i (axis_clk_i), + .cke_i (axis_cke_i), + .arst_i(axis_arst_i), + .rst_i (axis_sw_rst), + .en_i (axis_sw_enable), + .data_i(axis_pc_nxt), + .data_o(axis_pc) + ); + + // sent words counter + iob_counter #( + .DATA_W (DATA_W), + .RST_VAL({DATA_W{1'd0}}) + ) word_count_inst ( + .clk_i (axis_clk_i), + .cke_i (axis_cke_i), + .arst_i(axis_arst_i), + .rst_i (axis_sw_rst), + .en_i (axis_fifo_read), + .data_o(axis_word_count) + ); + + + //Synchronizers from sw_regs to axis domain + iob_sync #( + .DATA_W (1), + .RST_VAL(1'd0) + ) sw_rst ( + .clk_i (axis_clk_i), + .arst_i (axis_arst_i), + .signal_i(SOFT_RESET_wr), + .signal_o(axis_sw_rst) + ); + + iob_sync #( + .DATA_W (1), + .RST_VAL(1'd0) + ) sw_enable ( + .clk_i (axis_clk_i), + .arst_i (axis_arst_i), + .signal_i(ENABLE_wr), + .signal_o(axis_sw_enable) + ); + + iob_sync #( + .DATA_W (DATA_W), + .RST_VAL(0) + ) fifo_threshold ( + .clk_i (axis_clk_i), + .arst_i (axis_arst_i), + .signal_i(NWORDS_wr), + .signal_o(axis_nwords) + ); + + //FIFOs RAMs + genvar p; + generate + for (p = 0; p < R; p = p + 1) begin : gen_fifo_ram + // fifo memories + iob_ram_t2p #( + .DATA_W(TDATA_W), + .ADDR_W(RAM_ADDR_W) + ) iob_ram_t2p ( + .w_clk_i (ext_mem_w_clk), + .w_en_i (ext_mem_w_en[p]), + .w_addr_i(ext_mem_w_addr), + .w_data_i(ext_mem_w_data[p*TDATA_W+:TDATA_W]), + + .r_clk_i (ext_mem_r_clk), + .r_en_i (ext_mem_r_en[p]), + .r_addr_i(ext_mem_r_addr), + .r_data_o(ext_mem_r_data[p*TDATA_W+:TDATA_W]) + ); + end + endgenerate + + //async fifo + iob_fifo_async #( + .W_DATA_W(DATA_W), + .R_DATA_W(TDATA_W), + .ADDR_W (FIFO_ADDR_W) + ) data_fifo ( + //memory write port + .ext_mem_w_clk_o (ext_mem_w_clk), + .ext_mem_w_en_o (ext_mem_w_en), + .ext_mem_w_addr_o(ext_mem_w_addr), + .ext_mem_w_data_o(ext_mem_w_data), + //memory read port + .ext_mem_r_clk_o (ext_mem_r_clk), + .ext_mem_r_en_o (ext_mem_r_en), + .ext_mem_r_addr_o(ext_mem_r_addr), + .ext_mem_r_data_i(ext_mem_r_data), + //read port (axis clk domain) + .r_clk_i (axis_clk_i), + .r_cke_i (axis_cke_i), + .r_arst_i (axis_arst_i), + .r_rst_i (axis_sw_rst), + .r_en_i (axis_fifo_read), + .r_data_o (axis_tdata), + .r_empty_o (axis_fifo_empty), + .r_full_o (), + .r_level_o (), + //write port (sys clk domain) + .w_clk_i (clk_i), + .w_cke_i (cke_i), + .w_arst_i (arst_i), + .w_rst_i (SOFT_RESET_wr), + .w_en_i (fifo_write), + .w_data_i (fifo_wdata), + .w_empty_o (FIFO_EMPTY_rd), + .w_full_o (FIFO_FULL_rd), + .w_level_o (FIFO_LEVEL_rd) + ); + +endmodule diff --git a/submodules/LIB/hardware/modules/iob_axistream/axistream_out/iob_axistream_out.py b/submodules/LIB/hardware/modules/iob_axistream/axistream_out/iob_axistream_out.py new file mode 100755 index 000000000..28bbff094 --- /dev/null +++ b/submodules/LIB/hardware/modules/iob_axistream/axistream_out/iob_axistream_out.py @@ -0,0 +1,284 @@ +#!/usr/bin/env python3 + +import os + +from iob_module import iob_module + +# Submodules +from iob_fifo_async import iob_fifo_async +from iob_reg_re import iob_reg_re +from iob_ram_t2p import iob_ram_t2p +from iob_sync import iob_sync +from iob_counter import iob_counter + + +class iob_axistream_out(iob_module): + name = "iob_axistream_out" + version = "V0.30" + setup_dir = os.path.dirname(__file__) + + @classmethod + def _create_submodules_list(cls): + """Create submodules list with dependencies of this module""" + super()._create_submodules_list( + [ + {"interface": "iob_s_port"}, + {"interface": "iob_s_portmap"}, + iob_fifo_async, + iob_sync, + iob_reg_re, + iob_ram_t2p, + iob_counter, + ] + ) + + @classmethod + def _setup_confs(cls): + super()._setup_confs( + [ + # Macros + # Parameters + { + "name": "DATA_W", + "type": "P", + "val": "32", + "min": "32", + "max": "32", + "descr": "CPU data bus width", + }, + { + "name": "ADDR_W", + "type": "P", + "val": "`IOB_AXISTREAM_OUT_SWREG_ADDR_W", + "min": "NA", + "max": "NA", + "descr": "Address bus width", + }, + { + "name": "TDATA_W", + "type": "P", + "val": "8", + "min": "1", + "max": "DATA_W", + "descr": "AXI stream data width", + }, + { + "name": "FIFO_ADDR_W", + "type": "P", + "val": "4", + "min": "NA", + "max": "16", + "descr": "FIFO depth (log2)", + }, + ] + ) + + @classmethod + def _setup_ios(cls): + cls.ios += [ + {"name": "iob_s_port", "descr": "CPU native interface", "ports": []}, + { + "name": "general", + "descr": "System general interface signals", + "ports": [ + { + "name": "clk_i", + "type": "I", + "n_bits": "1", + "descr": "System clock input", + }, + { + "name": "cke_i", + "type": "I", + "n_bits": "1", + "descr": "IOb clock enable signal.", + }, + { + "name": "arst_i", + "type": "I", + "n_bits": "1", + "descr": "IOb reset, asynchronous and active high", + }, + { + "name": "interrupt_o", + "type": "O", + "n_bits": "1", + "descr": "FIFO threshold interrupt signal", + }, + ], + }, + { + "name": "axistream", + "descr": "AXI Stream interface signals", + "ports": [ + { + "name": "axis_clk_i", + "type": "I", + "n_bits": "1", + "descr": "Clock.", + }, + { + "name": "axis_cke_i", + "type": "I", + "n_bits": "1", + "descr": "Clock enable.", + }, + { + "name": "axis_arst_i", + "type": "I", + "n_bits": "1", + "descr": "Aynchronous and active high reset.", + }, + { + "name": "axis_tdata_o", + "type": "O", + "n_bits": "TDATA_W", + "descr": "Data.", + }, + { + "name": "axis_tvalid_o", + "type": "O", + "n_bits": "1", + "descr": "Valid.", + }, + { + "name": "axis_tready_i", + "type": "I", + "n_bits": "1", + "descr": "Ready.", + }, + { + "name": "axis_tlast_o", + "type": "O", + "n_bits": "1", + "descr": "Last word.", + }, + ], + }, + { + "name": "sys_axis", + "descr": "System AXI Stream interface.", + "ports": [ + { + "name": "sys_tdata_i", + "type": "I", + "n_bits": "DATA_W", + "descr": "Data.", + }, + { + "name": "sys_tvalid_i", + "type": "I", + "n_bits": "1", + "descr": "Valid.", + }, + { + "name": "sys_tready_o", + "type": "O", + "n_bits": "1", + "descr": "Ready.", + }, + ], + }, + ] + + @classmethod + def _setup_regs(cls): + cls.regs += [ + { + "name": "axistream", + "descr": "AXI Stream software accessible registers.", + "regs": [ + { + "name": "SOFT_RESET", + "type": "W", + "n_bits": 1, + "rst_val": 0, + "log2n_items": 0, + "autoreg": True, + "descr": "Soft reset.", + }, + { + "name": "ENABLE", + "type": "W", + "n_bits": 1, + "rst_val": 0, + "log2n_items": 0, + "autoreg": True, + "descr": "Enable peripheral.", + }, + { + "name": "DATA", + "type": "W", + "n_bits": 32, + "rst_val": 0, + "log2n_items": 0, + "autoreg": False, + "descr": "Data input.", + }, + { + "name": "MODE", + "type": "W", + "n_bits": "1", + "rst_val": 0, + "log2n_items": 0, + "autoreg": True, + "descr": "Sets the operation mode: (0) data is read using CSR; (1) data is read using system axistream interface.", + }, + { + "name": "NWORDS", + "type": "W", + "n_bits": "DATA_W", + "rst_val": 0, + "log2n_items": 0, + "autoreg": True, + "descr": "Set the number of words (with TDATA_W bits) to be written to the FIFO.", + }, + ], + }, + { + "name": "fifo", + "descr": "FIFO related registers", + "regs": [ + { + "name": "FIFO_FULL", + "type": "R", + "n_bits": 1, + "rst_val": 0, + "log2n_items": 0, + "autoreg": True, + "descr": "Full (1), or non-full (0).", + }, + { + "name": "FIFO_EMPTY", + "type": "R", + "n_bits": 1, + "rst_val": 0, + "log2n_items": 0, + "autoreg": True, + "descr": "Full (1), or non-full (0).", + }, + { + "name": "FIFO_THRESHOLD", + "type": "W", + "n_bits": "FIFO_ADDR_W+1", + "rst_val": 8, + "log2n_items": 0, + "autoreg": True, + "descr": "FIFO threshold level for interrupt signal", + }, + { + "name": "FIFO_LEVEL", + "type": "R", + "n_bits": "FIFO_ADDR_W+1", + "rst_val": 0, + "log2n_items": 0, + "autoreg": True, + "descr": "Current FIFO level", + }, + ], + }, + ] + + @classmethod + def _setup_block_groups(cls): + cls.block_groups += [] diff --git a/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/linux/Readme.md b/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/linux/Readme.md new file mode 100644 index 000000000..ac3982356 --- /dev/null +++ b/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/linux/Readme.md @@ -0,0 +1,15 @@ +# IOb AXIStream Out Linux Kernel Drivers +- Structure: + - `drivers/`: directory with linux kernel module drivers for + iob_axistream_out + - `iob_axistream_out_main.c`: driver source + - `[iob_axistream_out.h]` and `[iob_axistream_out_sysfs.h]`: header files + generated by: + ```bash + python3 .path/to/iob-linux/scripts/drivers.py iob_axistream_out -o [output_dir] + ``` + - `driver.mk`: makefile segment with `iob_axistream_out-obj:` target for driver + compilation + - `iob_axistream_out.dts`: device tree template with iob_axistream_out node + - manually add the `axistream_out` node to the system device tree so the + iob_axistream_out is recognized by the linux kernel diff --git a/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/linux/drivers/driver.mk b/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/linux/drivers/driver.mk new file mode 100644 index 000000000..14f885e16 --- /dev/null +++ b/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/linux/drivers/driver.mk @@ -0,0 +1 @@ +iob_axistream_out-objs := iob_axistream_out_main.o iob_class/iob_class_utils.o diff --git a/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/linux/drivers/iob_axistream_out_main.c b/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/linux/drivers/iob_axistream_out_main.c new file mode 100644 index 000000000..76bf04d35 --- /dev/null +++ b/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/linux/drivers/iob_axistream_out_main.c @@ -0,0 +1,359 @@ +/* iob_axistream_out_main.c: driver for iob_axistream_out + * using device platform. No hardcoded hardware address: + * 1. load driver: insmod iob_axistream_out.ko + * 2. run user app: ./user/user + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "iob_axistream_out.h" +#include "iob_class/iob_class_utils.h" + +static int iob_axistream_out_probe(struct platform_device *); +static int iob_axistream_out_remove(struct platform_device *); + +static ssize_t iob_axistream_out_read(struct file *, char __user *, size_t, + loff_t *); +static ssize_t iob_axistream_out_write(struct file *, const char __user *, + size_t, loff_t *); +static loff_t iob_axistream_out_llseek(struct file *, loff_t, int); +static int iob_axistream_out_open(struct inode *, struct file *); +static int iob_axistream_out_release(struct inode *, struct file *); + +static struct iob_data iob_axistream_out_data = {0}; +DEFINE_MUTEX(iob_axistream_out_mutex); + +#include "iob_axistream_out_sysfs.h" + +static const struct file_operations iob_axistream_out_fops = { + .owner = THIS_MODULE, + .write = iob_axistream_out_write, + .read = iob_axistream_out_read, + .llseek = iob_axistream_out_llseek, + .open = iob_axistream_out_open, + .release = iob_axistream_out_release, +}; + +static const struct of_device_id of_iob_axistream_out_match[] = { + {.compatible = "iobundle,axistream_out0"}, + {}, +}; + +static struct platform_driver iob_axistream_out_driver = { + .driver = + { + .name = "iob_axistream_out", + .owner = THIS_MODULE, + .of_match_table = of_iob_axistream_out_match, + }, + .probe = iob_axistream_out_probe, + .remove = iob_axistream_out_remove, +}; + +// +// Module init and exit functions +// +static int iob_axistream_out_probe(struct platform_device *pdev) { + struct resource *res; + int result = 0; + + if (iob_axistream_out_data.device != NULL) { + pr_err("[Driver] %s: No more devices allowed!\n", + IOB_AXISTREAM_OUT_DRIVER_NAME); + + return -ENODEV; + } + + pr_info("[Driver] %s: probing.\n", IOB_AXISTREAM_OUT_DRIVER_NAME); + + // Get the I/O region base address + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + pr_err("[Driver]: Failed to get I/O resource!\n"); + result = -ENODEV; + goto r_get_resource; + } + + // Request and map the I/O region + iob_axistream_out_data.regbase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(iob_axistream_out_data.regbase)) { + result = PTR_ERR(iob_axistream_out_data.regbase); + goto r_ioremmap; + } + iob_axistream_out_data.regsize = resource_size(res); + + // Alocate char device + result = alloc_chrdev_region(&iob_axistream_out_data.devnum, 0, 1, + IOB_AXISTREAM_OUT_DRIVER_NAME); + if (result) { + pr_err("%s: Failed to allocate device number!\n", + IOB_AXISTREAM_OUT_DRIVER_NAME); + goto r_alloc_region; + } + + cdev_init(&iob_axistream_out_data.cdev, &iob_axistream_out_fops); + + result = + cdev_add(&iob_axistream_out_data.cdev, iob_axistream_out_data.devnum, 1); + if (result) { + pr_err("%s: Char device registration failed!\n", + IOB_AXISTREAM_OUT_DRIVER_NAME); + goto r_cdev_add; + } + + // Create device class // todo: make a dummy driver just to create and own the + // class: https://stackoverflow.com/a/16365027/8228163 + if ((iob_axistream_out_data.class = + class_create(THIS_MODULE, IOB_AXISTREAM_OUT_DRIVER_CLASS)) == NULL) { + printk("Device class can not be created!\n"); + goto r_class; + } + + // Create device file + iob_axistream_out_data.device = device_create( + iob_axistream_out_data.class, NULL, iob_axistream_out_data.devnum, NULL, + IOB_AXISTREAM_OUT_DRIVER_NAME); + if (iob_axistream_out_data.device == NULL) { + printk("Can not create device file!\n"); + goto r_device; + } + + result = + iob_axistream_out_create_device_attr_files(iob_axistream_out_data.device); + if (result) { + pr_err("Cannot create device attribute file......\n"); + goto r_dev_file; + } + + dev_info(&pdev->dev, "initialized.\n"); + goto r_ok; + +r_dev_file: + iob_axistream_out_remove_device_attr_files(&iob_axistream_out_data); +r_device: + class_destroy(iob_axistream_out_data.class); +r_class: + cdev_del(&iob_axistream_out_data.cdev); +r_cdev_add: + unregister_chrdev_region(iob_axistream_out_data.devnum, 1); +r_alloc_region: + // iounmap is managed by devm +r_ioremmap: +r_get_resource: +r_ok: + + return result; +} + +static int iob_axistream_out_remove(struct platform_device *pdev) { + iob_axistream_out_remove_device_attr_files(&iob_axistream_out_data); + class_destroy(iob_axistream_out_data.class); + cdev_del(&iob_axistream_out_data.cdev); + unregister_chrdev_region(iob_axistream_out_data.devnum, 1); + // Note: no need for iounmap, since we are using devm_ioremap_resource() + + dev_info(&pdev->dev, "exiting.\n"); + + return 0; +} + +static int __init iob_axistream_out_init(void) { + pr_info("[Driver] %s: initializing.\n", IOB_AXISTREAM_OUT_DRIVER_NAME); + + return platform_driver_register(&iob_axistream_out_driver); +} + +static void __exit iob_axistream_out_exit(void) { + pr_info("[Driver] %s: exiting.\n", IOB_AXISTREAM_OUT_DRIVER_NAME); + platform_driver_unregister(&iob_axistream_out_driver); +} + +// +// File operations +// + +static int iob_axistream_out_open(struct inode *inode, struct file *file) { + pr_info("[Driver] iob_axistream_out device opened\n"); + + if (!mutex_trylock(&iob_axistream_out_mutex)) { + pr_info("Another process is accessing the device\n"); + + return -EBUSY; + } + + return 0; +} + +static int iob_axistream_out_release(struct inode *inode, struct file *file) { + pr_info("[Driver] iob_axistream_out device closed\n"); + + mutex_unlock(&iob_axistream_out_mutex); + + return 0; +} + +static ssize_t iob_axistream_out_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) { + int size = 0; + u32 value = 0; + + /* read value from register */ + switch (*ppos) { + case IOB_AXISTREAM_OUT_FIFO_FULL_ADDR: + value = iob_data_read_reg(iob_axistream_out_data.regbase, + IOB_AXISTREAM_OUT_FIFO_FULL_ADDR, + IOB_AXISTREAM_OUT_FIFO_FULL_W); + size = (IOB_AXISTREAM_OUT_FIFO_FULL_W >> 3); // bit to bytes + pr_info("[Driver] Read FIFO_FULL!\n"); + break; + case IOB_AXISTREAM_OUT_FIFO_EMPTY_ADDR: + value = iob_data_read_reg(iob_axistream_out_data.regbase, + IOB_AXISTREAM_OUT_FIFO_EMPTY_ADDR, + IOB_AXISTREAM_OUT_FIFO_EMPTY_W); + size = (IOB_AXISTREAM_OUT_FIFO_EMPTY_W >> 3); // bit to bytes + pr_info("[Driver] Read FIFO_EMPTY!\n"); + break; + case IOB_AXISTREAM_OUT_FIFO_LEVEL_ADDR: + value = iob_data_read_reg(iob_axistream_out_data.regbase, + IOB_AXISTREAM_OUT_FIFO_LEVEL_ADDR, + IOB_AXISTREAM_OUT_FIFO_LEVEL_W); + size = (IOB_AXISTREAM_OUT_FIFO_LEVEL_W >> 3); // bit to bytes + pr_info("[Driver] Read FIFO_LEVEL!\n"); + break; + case IOB_AXISTREAM_OUT_VERSION_ADDR: + value = iob_data_read_reg(iob_axistream_out_data.regbase, + IOB_AXISTREAM_OUT_VERSION_ADDR, + IOB_AXISTREAM_OUT_VERSION_W); + size = (IOB_AXISTREAM_OUT_VERSION_W >> 3); // bit to bytes + pr_info("[Driver] Read version!\n"); + break; + default: + // invalid address - no bytes read + return 0; + } + + // Read min between count and REG_SIZE + if (size > count) + size = count; + + if (copy_to_user(buf, &value, size)) + return -EFAULT; + + return count; +} + +static ssize_t iob_axistream_out_write(struct file *file, + const char __user *buf, size_t count, + loff_t *ppos) { + int size = 0; + u32 value = 0; + + switch (*ppos) { + case IOB_AXISTREAM_OUT_SOFT_RESET_ADDR: + size = (IOB_AXISTREAM_OUT_SOFT_RESET_W >> 3); // bit to bytes + if (read_user_data(buf, size, &value)) + return -EFAULT; + iob_data_write_reg(iob_axistream_out_data.regbase, value, + IOB_AXISTREAM_OUT_SOFT_RESET_ADDR, + IOB_AXISTREAM_OUT_SOFT_RESET_W); + pr_info("[Driver] SOFT_RESET iob_axistream_out: 0x%x\n", value); + break; + case IOB_AXISTREAM_OUT_ENABLE_ADDR: + size = (IOB_AXISTREAM_OUT_ENABLE_W >> 3); // bit to bytes + if (read_user_data(buf, size, &value)) + return -EFAULT; + iob_data_write_reg(iob_axistream_out_data.regbase, value, + IOB_AXISTREAM_OUT_ENABLE_ADDR, + IOB_AXISTREAM_OUT_ENABLE_W); + pr_info("[Driver] ENABLE iob_axistream_out: 0x%x\n", value); + break; + case IOB_AXISTREAM_OUT_DATA_ADDR: + size = (IOB_AXISTREAM_OUT_DATA_W >> 3); // bit to bytes + if (read_user_data(buf, size, &value)) + return -EFAULT; + iob_data_write_reg(iob_axistream_out_data.regbase, value, + IOB_AXISTREAM_OUT_DATA_ADDR, IOB_AXISTREAM_OUT_DATA_W); + pr_info("[Driver] DATA iob_axistream_out: 0x%x\n", value); + break; + case IOB_AXISTREAM_OUT_MODE_ADDR: + size = (IOB_AXISTREAM_OUT_MODE_W >> 3); // bit to bytes + if (read_user_data(buf, size, &value)) + return -EFAULT; + iob_data_write_reg(iob_axistream_out_data.regbase, value, + IOB_AXISTREAM_OUT_MODE_ADDR, IOB_AXISTREAM_OUT_MODE_W); + pr_info("[Driver] MODE iob_axistream_out: 0x%x\n", value); + break; + case IOB_AXISTREAM_OUT_NWORDS_ADDR: + size = (IOB_AXISTREAM_OUT_NWORDS_W >> 3); // bit to bytes + if (read_user_data(buf, size, &value)) + return -EFAULT; + iob_data_write_reg(iob_axistream_out_data.regbase, value, + IOB_AXISTREAM_OUT_NWORDS_ADDR, + IOB_AXISTREAM_OUT_NWORDS_W); + pr_info("[Driver] NWORDS iob_axistream_out: 0x%x\n", value); + break; + case IOB_AXISTREAM_OUT_FIFO_THRESHOLD_ADDR: + size = (IOB_AXISTREAM_OUT_FIFO_THRESHOLD_W >> 3); // bit to bytes + if (read_user_data(buf, size, &value)) + return -EFAULT; + iob_data_write_reg(iob_axistream_out_data.regbase, value, + IOB_AXISTREAM_OUT_FIFO_THRESHOLD_ADDR, + IOB_AXISTREAM_OUT_FIFO_THRESHOLD_W); + pr_info("[Driver] FIFO_THRESHOLD iob_axistream_out: 0x%x\n", value); + break; + default: + pr_info("[Driver] Invalid write address 0x%x\n", (unsigned int)*ppos); + // invalid address - no bytes written + return 0; + } + + return count; +} + +/* Custom lseek function + * check: lseek(2) man page for whence modes + */ +static loff_t iob_axistream_out_llseek(struct file *filp, loff_t offset, + int whence) { + loff_t new_pos = -1; + + switch (whence) { + case SEEK_SET: + new_pos = offset; + break; + case SEEK_CUR: + new_pos = filp->f_pos + offset; + break; + case SEEK_END: + new_pos = (1 << IOB_AXISTREAM_OUT_SWREG_ADDR_W) + offset; + break; + default: + return -EINVAL; + } + + // Check for valid bounds + if (new_pos < 0 || new_pos > iob_axistream_out_data.regsize) { + return -EINVAL; + } + + // Update file position + filp->f_pos = new_pos; + + return new_pos; +} + +module_init(iob_axistream_out_init); +module_exit(iob_axistream_out_exit); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_AUTHOR("IObundle"); +MODULE_DESCRIPTION("IOb-AXISTREAM-OUT Drivers"); +MODULE_VERSION("0.10"); diff --git a/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/linux/iob_axistream_out.dts b/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/linux/iob_axistream_out.dts new file mode 100644 index 000000000..7cffd051d --- /dev/null +++ b/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/linux/iob_axistream_out.dts @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* Copyright (c) 2024 IObundle */ + +/dts-v1/; + +/ { + #address-cells = <1>; + #size-cells = <1>; + model = "IOb-SoC, VexRiscv"; + compatible = "IOb-SoC, VexRiscv"; + // CPU + // Memory + // Choosen + soc { + #address-cells = <1>; + #size-cells = <1>; + compatible = "iobundle,iob-soc", "simple-bus"; + ranges; + + // Other SOC peripherals go here + + // Add this Node to the device tree + AXISTREAMOUT0: axistream_out@/*AXISTREAMOUT0_ADDR_MACRO*/ { + compatible = "iobundle,axistream_out0"; + reg = <0x/*AXISTREAMOUT0_ADDR_MACRO*/ 0x20>; + }; + + }; +}; diff --git a/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/src/iob-axistream-out.c b/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/src/iob-axistream-out.c new file mode 100644 index 000000000..dc86bb16f --- /dev/null +++ b/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/src/iob-axistream-out.c @@ -0,0 +1,15 @@ +#include "iob-axistream-out.h" + +void iob_axis_out_reset() { + IOB_AXISTREAM_OUT_SET_SOFT_RESET(1); + IOB_AXISTREAM_OUT_SET_SOFT_RESET(0); +} + +uint32_t iob_axis_write(uint32_t value) { + if (IOB_AXISTREAM_OUT_GET_FIFO_FULL()) { + return 0; + } else { + IOB_AXISTREAM_OUT_SET_DATA(value); + return 1; + } +} diff --git a/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/src/iob-axistream-out.h b/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/src/iob-axistream-out.h new file mode 100644 index 000000000..69a363ff5 --- /dev/null +++ b/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/src/iob-axistream-out.h @@ -0,0 +1,5 @@ +#include "iob_axistream_out_swreg.h" + +void iob_axis_out_reset(); + +uint32_t iob_axis_write(uint32_t value); diff --git a/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/src/iob_axistream_out_swreg_pc_emul.c b/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/src/iob_axistream_out_swreg_pc_emul.c new file mode 100644 index 000000000..ba050be36 --- /dev/null +++ b/submodules/LIB/hardware/modules/iob_axistream/axistream_out/software/src/iob_axistream_out_swreg_pc_emul.c @@ -0,0 +1,28 @@ +/* PC Emulation of axistream-out peripheral */ + +#include + +#include "iob_axistream_out_swreg.h" + +// Base Address +static int base; +void IOB_AXISTREAM_OUT_INIT_BASEADDR(uint32_t addr) { base = addr; } + +// Core Setters and Getters +void IOB_AXISTREAM_OUT_SET_DATA(uint32_t value) {} + +uint8_t IOB_AXISTREAM_OUT_GET_FULL() { return 0x00; } + +void IOB_AXISTREAM_OUT_SET_SOFT_RESET(uint8_t value) {} + +void IOB_AXISTREAM_OUT_SET_ENABLE(uint8_t value) {} + +void IOB_AXISTREAM_OUT_SET_WSTRB(uint8_t value) {} + +void IOB_AXISTREAM_OUT_SET_LAST(uint8_t value) {} + +void IOB_AXISTREAM_OUT_SET_FIFO_THRESHOLD(uint32_t value) {} + +uint32_t IOB_AXISTREAM_OUT_GET_FIFO_LEVEL() { return 0x00; } + +uint16_t IOB_AXISTREAM_OUT_GET_VERSION() { return 0xaaaa; } diff --git a/submodules/LIB/hardware/modules/iob_nco/hardware/simulation/src/iob_nco_tb.v b/submodules/LIB/hardware/modules/iob_nco/hardware/simulation/src/iob_nco_tb.v index 829b864b5..ffceb901a 100644 --- a/submodules/LIB/hardware/modules/iob_nco/hardware/simulation/src/iob_nco_tb.v +++ b/submodules/LIB/hardware/modules/iob_nco/hardware/simulation/src/iob_nco_tb.v @@ -20,14 +20,17 @@ module iob_nco_tb; integer fd; localparam CLK_PER = 10; + localparam CLK2_PER = 12; localparam ADDR_W = `IOB_NCO_SWREG_ADDR_W; localparam DATA_W = 32; reg clk = 1; + reg clk_in = 1; // Drive clock always #(CLK_PER / 2) clk = ~clk; + always #(CLK2_PER / 2) clk_in = ~clk_in; reg cke = 1'b1; reg arst; @@ -65,7 +68,8 @@ module iob_nco_tb; IOB_NCO_SET_SOFT_RESET(1'b1); IOB_NCO_SET_SOFT_RESET(1'b0); - IOB_NCO_SET_PERIOD(16'h1280); + IOB_NCO_SET_PERIOD_INT(32'h12); + IOB_NCO_SET_PERIOD_FRAC(32'h80000000); IOB_NCO_SET_ENABLE(1'b1); $display("%c[1;34m", 27); @@ -80,6 +84,9 @@ module iob_nco_tb; iob_nco nco ( `include "clk_en_rst_s_portmap.vs" + .clk_in_i(clk_in), + .clk_in_arst_i(arst), + .clk_in_cke_i(1'b1), .iob_valid_i(iob_valid_i), .iob_addr_i(iob_addr_i), .iob_wdata_i(iob_wdata_i), @@ -87,7 +94,7 @@ module iob_nco_tb; .iob_rdata_o(iob_rdata_o), .iob_ready_o(iob_ready_o), .iob_rvalid_o(iob_rvalid_o), - .clk_o(clk_out) + .clk_out_o(clk_out) ); `include "iob_nco_swreg_emb_tb.vs" diff --git a/submodules/LIB/hardware/modules/iob_nco/hardware/src/iob_nco.v b/submodules/LIB/hardware/modules/iob_nco/hardware/src/iob_nco.v index dfd26fc11..7181f00e2 100644 --- a/submodules/LIB/hardware/modules/iob_nco/hardware/src/iob_nco.v +++ b/submodules/LIB/hardware/modules/iob_nco/hardware/src/iob_nco.v @@ -8,42 +8,122 @@ module iob_nco #( `include "iob_nco_io.vs" ); - wire [ DATA_W-1:0] period_r; - wire [ DATA_W-1:0] diff; - wire [DATA_W-1:FRAC_W] cnt; - wire [DATA_W-1:0] acc_in, acc_out; +// USING NCO FROM ASRC submodules!!! + localparam PERIOD_W = 2*DATA_W; + localparam FRAC_W = DATA_W; + + wire [PERIOD_W-1:0] period_r; + wire [PERIOD_W-1:0] diff; + wire [DATA_W-1:0] cnt; + wire [PERIOD_W-1:0] acc_in, acc_out; wire clk_int; - //Dummy iob_ready_nxt_o and iob_rvalid_nxt_o to be used in swreg (unused ports) - wire iob_ready_nxt; - wire iob_rvalid_nxt; + wire soft_reset; + wire enable; + wire [PERIOD_W-1:0] period_wdata; + wire period_wen; //BLOCK Register File & Configuration, control and status registers accessible by the sofware `include "iob_nco_swreg_inst.vs" + // Concatenate Integer and fractional Period registers + wire [(2*DATA_W)-1:0] period_full_wdata; + wire period_full_wen; + // integer period value register + iob_reg_re #( + .DATA_W(DATA_W) + ) int_reg ( + .clk_i (clk_i), + .cke_i (cke_i), + .arst_i(arst_i), + .rst_i (SOFT_RESET_wr), + .en_i (PERIOD_INT_wen_wr), + .data_i(PERIOD_INT_wdata_wr), + .data_o(period_full_wdata[DATA_W+:DATA_W]) + ); + // fractional period value register + iob_reg_re #( + .DATA_W(DATA_W) + ) frac_reg ( + .clk_i (clk_i), + .cke_i (cke_i), + .arst_i(arst_i), + .rst_i (SOFT_RESET_wr), + .en_i (PERIOD_FRAC_wen_wr), + .data_i(PERIOD_FRAC_wdata_wr), + .data_o(period_full_wdata[0+:DATA_W]) + ); + // period valid register + wire per_valid_en; + wire per_valid_rst; + wire [2-1:0] per_valid_nxt; + wire [2-1:0] per_valid; + assign per_valid_en = PERIOD_INT_wen_wr | PERIOD_FRAC_wen_wr; + assign per_valid_rst = SOFT_RESET_wr | (&per_valid); + assign per_valid_nxt = per_valid | ({PERIOD_INT_wen_wr, PERIOD_FRAC_wen_wr}); + iob_reg_re #( + .DATA_W(2) + ) valid_reg ( + .clk_i (clk_i), + .cke_i (cke_i), + .arst_i(arst_i), + .rst_i (per_valid_rst), + .en_i (per_valid_en), + .data_i(per_valid_nxt), + .data_o(per_valid) + ); + assign period_full_wen = &per_valid; + + + iob_nco_sync #( + .PERIOD_W(PERIOD_W) + ) nco_sync_inst ( + .clk_i(clk_i), + .cke_i(cke_i), + .arst_i(arst_i), + + .clk_in_i(clk_in_i), + .clk_in_arst_i(clk_in_arst_i), + .clk_in_cke_i(clk_in_cke_i), + + .soft_reset_i(SOFT_RESET_wr), + .enable_i(ENABLE_wr), + .period_wdata_i(period_full_wdata), + .period_wen_i(period_full_wen), + + .soft_reset_o(soft_reset), + .enable_o(enable), + .period_wdata_o(period_wdata), + .period_wen_o(period_wen) + ); + + // PERIOD Manual logic - assign PERIOD_wready_wr = 1'b1; + assign PERIOD_INT_wready_wr = 1'b1; + assign PERIOD_FRAC_wready_wr = 1'b1; - reg [DATA_W-1:FRAC_W] quant; + reg [DATA_W-1:0] quant; assign diff = period_r - {quant, {FRAC_W{1'b0}}}; assign clk_int = (cnt > (quant / 2)); always @* begin if (acc_out[FRAC_W-1:0] == {1'b1, {FRAC_W - 1{1'b0}}}) - quant = acc_out[DATA_W-1:FRAC_W] + ^acc_out[DATA_W-1:FRAC_W]; - else if (acc_out[FRAC_W-1]) quant = acc_out[DATA_W-1:FRAC_W] + 1'b1; - else quant = acc_out[DATA_W-1:FRAC_W]; + quant = acc_out[PERIOD_W-1:FRAC_W] + ^acc_out[PERIOD_W-1:FRAC_W]; + else if (acc_out[FRAC_W-1]) quant = acc_out[PERIOD_W-1:FRAC_W] + 1'b1; + else quant = acc_out[PERIOD_W-1:FRAC_W]; end //fractional period value register iob_reg_re #( - .DATA_W(DATA_W) + .DATA_W(PERIOD_W) ) per_reg ( - `include "clk_en_rst_s_s_portmap.vs" - .rst_i (SOFT_RESET_wr), - .en_i (PERIOD_wen_wr), - .data_i(PERIOD_wdata_wr), + .clk_i (clk_in_i), + .cke_i (clk_in_cke_i), + .arst_i(clk_in_arst_i), + .rst_i (soft_reset), + .en_i (period_wen), + .data_i(period_wdata), .data_o(period_r) ); @@ -51,37 +131,42 @@ module iob_nco #( iob_reg_re #( .DATA_W(1) ) clk_out_reg ( - `include "clk_en_rst_s_s_portmap.vs" - .rst_i (SOFT_RESET_wr), - .en_i (ENABLE_wr), + .clk_i (clk_in_i), + .cke_i (clk_in_cke_i), + .arst_i(clk_in_arst_i), + .rst_i (soft_reset), + .en_i (enable), .data_i(clk_int), - .data_o(clk_o) + .data_o(clk_out_o) ); //modulator accumulator iob_acc_ld #( - .DATA_W(DATA_W) + .DATA_W(PERIOD_W) ) acc_ld ( - `include "clk_en_rst_s_s_portmap.vs" - .rst_i(SOFT_RESET_wr), - .en_i(ENABLE_wr), - .ld_i(PERIOD_wen_wr), - .ld_val_i(PERIOD_wdata_wr), + .clk_i (clk_in_i), + .cke_i (clk_in_cke_i), + .arst_i(clk_in_arst_i), + .rst_i(soft_reset), + .en_i(enable), + .ld_i(period_wen), + .ld_val_i(period_wdata), .incr_i(diff), - .data_o(acc_out) + .data_o(acc_out), + .data_nxt_o() ); - //output period counter iob_modcnt #( - .DATA_W(DATA_W - FRAC_W) + .DATA_W(DATA_W) ) modcnt ( - `include "clk_en_rst_s_s_portmap.vs" - .rst_i (PERIOD_wen_wr), - .en_i (ENABLE_wr), + .clk_i (clk_in_i), + .cke_i (clk_in_cke_i), + .arst_i(clk_in_arst_i), + .rst_i (period_wen), + .en_i (enable), .mod_i (quant), .data_o(cnt) ); - endmodule diff --git a/submodules/LIB/hardware/modules/iob_nco/hardware/src/iob_nco_sync.v b/submodules/LIB/hardware/modules/iob_nco/hardware/src/iob_nco_sync.v new file mode 100644 index 000000000..2a33cda59 --- /dev/null +++ b/submodules/LIB/hardware/modules/iob_nco/hardware/src/iob_nco_sync.v @@ -0,0 +1,133 @@ +`timescale 1ns / 1ps +`include "iob_nco_conf.vh" +`include "iob_nco_swreg_def.vh" + +module iob_nco_sync #( + parameter PERIOD_W = 0 +) ( + input clk_i, + input cke_i, + input arst_i, + + input clk_in_i, + input clk_in_arst_i, + input clk_in_cke_i, + + input soft_reset_i, + input enable_i, + input [PERIOD_W-1:0] period_wdata_i, + input period_wen_i, + + output soft_reset_o, + output enable_o, + output [PERIOD_W-1:0] period_wdata_o, + output period_wen_o +); + + //synchronize CSR clock to clk_in_i clock + iob_sync #( + .DATA_W(1), + .RST_VAL(1'b0) + ) soft_reset_sync ( + .clk_i (clk_in_i), + .arst_i (clk_in_arst_i), + .signal_i (soft_reset_i), + .signal_o (soft_reset_o) + ); + + iob_sync #( + .DATA_W(1), + .RST_VAL(1'b0) + ) enable_sync ( + .clk_i (clk_in_i), + .arst_i (clk_in_arst_i), + .signal_i (enable_i), + .signal_o (enable_o) + ); + + // async fifo memory signals + wire period_fifo_w_clk; + wire [PERIOD_W-1:0] period_fifo_w_data; + wire period_fifo_w_addr; + wire period_fifo_w_en; + wire period_fifo_r_clk; + wire [PERIOD_W-1:0] period_fifo_r_data; + wire period_fifo_r_addr; + wire period_fifo_r_en; + wire period_fifo_w_full; + + wire period_fifo_ren; + wire period_fifo_empty; + + assign period_fifo_ren = ~period_fifo_empty; + + iob_fifo_async #( + .W_DATA_W(PERIOD_W), + .R_DATA_W(PERIOD_W), + .ADDR_W(1) + ) period_fifo ( + //memory write port + .ext_mem_w_clk_o(period_fifo_w_clk), + .ext_mem_w_en_o(period_fifo_w_en), + .ext_mem_w_addr_o(period_fifo_w_addr), + .ext_mem_w_data_o(period_fifo_w_data), + //memory read port + .ext_mem_r_clk_o(period_fifo_r_clk), + .ext_mem_r_en_o(period_fifo_r_en), + .ext_mem_r_addr_o(period_fifo_r_addr), + .ext_mem_r_data_i(period_fifo_r_data), + + //read port + .r_clk_i(clk_in_i), + .r_cke_i(clk_in_cke_i), + .r_arst_i(clk_in_arst_i), + .r_rst_i(1'b0), + .r_en_i(period_fifo_ren), + .r_data_o(period_wdata_o), + .r_empty_o(period_fifo_empty), + .r_full_o(), + .r_level_o(), + + //write port + .w_clk_i(clk_i), + .w_cke_i(cke_i), + .w_arst_i(arst_i), + .w_rst_i(soft_reset_i), + .w_en_i(period_wen_i), + .w_data_i(period_wdata_i), + .w_empty_o(), + .w_full_o(), + .w_level_o() + ); + + // audio in FIFO memory + iob_regfile_at2p #( + .ADDR_W(1), + .DATA_W(PERIOD_W) + ) audio_in_fifo_regs ( + .w_clk_i (period_fifo_w_clk), + .w_cke_i (period_fifo_w_en), + .w_arst_i(arst_i), + .w_addr_i(period_fifo_w_addr), + .w_data_i(period_fifo_w_data), + .r_clk_i (period_fifo_r_clk), + .r_cke_i (period_fifo_r_en), + .r_arst_i(clk_in_arst_i), + .r_addr_i(period_fifo_r_addr), + .r_data_o(period_fifo_r_data) + ); + + //fractional period value register + iob_reg_r #( + .DATA_W(1), + .RST_VAL(1'b0) + ) period_wen_reg ( + .clk_i (clk_in_i), + .cke_i (clk_in_cke_i), + .arst_i(clk_in_arst_i), + .rst_i (1'b0), + .data_i(period_fifo_ren), + .data_o(period_wen_o) + ); + +endmodule diff --git a/submodules/LIB/hardware/modules/iob_nco/iob_nco.py b/submodules/LIB/hardware/modules/iob_nco/iob_nco.py index 0b991ba88..7c38c964d 100644 --- a/submodules/LIB/hardware/modules/iob_nco/iob_nco.py +++ b/submodules/LIB/hardware/modules/iob_nco/iob_nco.py @@ -3,12 +3,16 @@ from iob_module import iob_module from iob_tasks import iob_tasks -from iob_reg_e import iob_reg_e -from iob_reg_r import iob_reg_r from iob_reg import iob_reg +from iob_reg_r import iob_reg_r +from iob_reg_e import iob_reg_e +from iob_reg_re import iob_reg_re from iob_modcnt import iob_modcnt from iob_acc_ld import iob_acc_ld from iob_utils import iob_utils +from iob_sync import iob_sync +from iob_fifo_async import iob_fifo_async +from iob_regfile_at2p import iob_regfile_at2p class iob_nco(iob_module): @@ -22,9 +26,13 @@ def _create_submodules_list(cls): super()._create_submodules_list( [ iob_reg_r, + iob_reg_re, iob_reg, iob_modcnt, iob_acc_ld, + iob_sync, + iob_regfile_at2p, + iob_fifo_async, # simulation files (iob_utils, {"purpose": "simulation"}), (iob_tasks, {"purpose": "simulation"}), @@ -55,14 +63,6 @@ def _setup_confs(cls): "max": "32", "descr": "Address bus width", }, - { - "name": "FRAC_W", - "type": "P", - "val": "8", - "min": "0", - "max": "32", - "descr": "Bit-width of the fractional part of the period value. Used to differentiate between the integer and fractional parts of the period. ", - }, ] ) @@ -77,10 +77,28 @@ def _setup_ios(cls): {"name": "iob_s_port", "descr": "CPU native interface", "ports": []}, { "name": "clk_gen", - "descr": "Output generated clock interface", + "descr": "Generated clock interface", "ports": [ { - "name": "clk_o", + "name": "clk_in_i", + "type": "I", + "n_bits": "1", + "descr": "Clock input", + }, + { + "name": "clk_in_arst_i", + "type": "I", + "n_bits": "1", + "descr": "Clock input asynchronous reset", + }, + { + "name": "clk_in_cke_i", + "type": "I", + "n_bits": "1", + "descr": "Clock input enable", + }, + { + "name": "clk_out_o", "type": "O", "n_bits": "1", "descr": "Generated clock output", @@ -115,13 +133,22 @@ def _setup_regs(cls): "descr": "NCO enable", }, { - "name": "PERIOD", + "name": "PERIOD_INT", "type": "W", "n_bits": 32, "rst_val": 5, "log2n_items": 0, "autoreg": False, - "descr": "Period of the generated clock in terms of the number of system clock cycles + 1 implicit clock cycle. The period value is divided into integer and fractional parts where the lower FRAC_W bits represent the fractional part, and the remaining upper bits represent the integer part.", + "descr": "Integer part of the generated period. Period of the generated clock in terms of the number of system clock cycles + 1 implicit clock cycle. NOTE: need to write to both PERIOD_INT, PERIOD_FRAC registers to set internal period.", + }, + { + "name": "PERIOD_FRAC", + "type": "W", + "n_bits": 32, + "rst_val": 0, + "log2n_items": 0, + "autoreg": False, + "descr": "Fractional part of the generated period. NOTE: need to write to both PERIOD_INT, PERIOD_FRAC registers to set internal period.", }, ], } diff --git a/submodules/LIB/hardware/modules/iob_nco/software/src/iob-nco.c b/submodules/LIB/hardware/modules/iob_nco/software/src/iob-nco.c index b6d2df540..8acf0cf08 100644 --- a/submodules/LIB/hardware/modules/iob_nco/software/src/iob-nco.c +++ b/submodules/LIB/hardware/modules/iob_nco/software/src/iob-nco.c @@ -17,6 +17,11 @@ void nco_init(uint32_t base_address) { void nco_enable(bool enable) { IOB_NCO_SET_ENABLE(enable); } // Configure NCO output signal period to be 'period'+1 times the system clock -// period. Iob_NCO always assumes +1 clock period implicitly. Lowest 8 bits of +// period. Iob_NCO always assumes +1 clock period implicitly. Lowest 32 bits of // value are the fractional part of the period by default -void nco_set_period(uint32_t period) { IOB_NCO_SET_PERIOD(period); } +void nco_set_period(uint64_t period) { + uint32_t period_int = (uint32_t)(period >> 32); + uint32_t period_frac = (uint32_t)(period && (0xFFFFFFFF)); + IOB_NCO_SET_PERIOD_INT(period_int); + IOB_NCO_SET_PERIOD_FRAC(period_frac); +} diff --git a/submodules/LIB/hardware/modules/iob_nco/software/src/iob-nco.h b/submodules/LIB/hardware/modules/iob_nco/software/src/iob-nco.h index 36c0c55a8..16dc543c5 100644 --- a/submodules/LIB/hardware/modules/iob_nco/software/src/iob-nco.h +++ b/submodules/LIB/hardware/modules/iob_nco/software/src/iob-nco.h @@ -6,4 +6,4 @@ void nco_reset(); void nco_init(uint32_t base_address); void nco_enable(bool enable); -void nco_set_period(uint32_t period); +void nco_set_period(uint64_t period);