/* * ut_dma_wr.v * * DMA Read (Mem -> USB) * * vim: ts=4 sw=4 * * Copyright (C) 2022 Sylvain Munaut * SPDX-License-Identifier: CERN-OHL-P-2.0 */ `default_nettype none module ut_dma_wr ( // Bus interface input wire [1:0] bus_addr, output wire [31:0] bus_rdata, input wire [31:0] bus_wdata, input wire bus_we_csr, input wire bus_we_desc_in, input wire bus_re_desc_out, // Stream push input wire [7:0] stream_data, input wire stream_stb, // Memory interface output wire [23:0] mi_addr, output reg [ 6:0] mi_len, output wire mi_valid, input wire mi_ready, output wire [7:0] mi_wdata, input wire mi_wack, input wire mi_wlast, // Clock / Reset input wire clk, input wire rst ); // Signals // ------- // Input Data FIFO wire [7:0] df_wdata; wire df_we; wire df_wex; wire df_full; wire [7:0] df_rdata; wire df_re; wire df_empty; reg df_ovfl; wire df_ovfl_set; reg df_ovfl_clr; (* keep *) wire [1:0] df_level_move; reg [9:0] df_level; // Descriptors FIFOs wire [30:0] dif_wdata; wire dif_we; wire dif_wex; wire dif_full; wire [30:0] dif_rdata; wire dif_re; wire dif_empty; reg dif_ovfl; wire dif_ovfl_set; reg dif_ovfl_clr; reg dif_unfl; wire dif_unfl_set; reg dif_unfl_clr; wire [30:0] dof_wdata; wire dof_we; wire dof_wex; wire dof_full; wire [30:0] dof_rdata; wire dof_re; wire dof_empty; reg dof_empty_r; reg dof_ovfl; wire dof_ovfl_set; reg dof_ovfl_clr; // Control reg ctrl_dif_flush; reg ctrl_ovfl_dis; reg ctrl_flush; reg ctrl_active; reg ctrl_disable_stb; // FSM localparam [2:0] ST_DISABLED = 0, ST_WAIT_BURST = 1, ST_ISSUE = 2, ST_DATA = 3, ST_DONE = 4; reg [2:0] state; reg [2:0] state_nxt; // Current Descriptor wire [23:0] cd_addr; wire [6:0] cd_len; wire cd_valid; // Level thresholds reg [10:0] lvl_cmp; wire lvl_enough; reg lvl_zero; // Bus interface // ------------- // Read mux assign bus_rdata = bus_addr[1] ? // Descriptor Out Read { dof_empty, dof_rdata } : // CSR Read { df_ovfl, 1'b0, df_full, df_empty, 2'b00, df_level, dif_ovfl, dif_unfl, dif_full, dif_empty, dof_ovfl, 1'b0, dof_full, dof_empty, 5'b00000, ctrl_ovfl_dis, ctrl_flush, ctrl_active }; // CSR Write always @(posedge clk or posedge rst) if (rst) ctrl_active <= 1'b0; else ctrl_active <= bus_we_csr ? bus_wdata[0] : (ctrl_active & ~ctrl_disable_stb); always @(posedge clk or posedge rst) if (rst) begin ctrl_ovfl_dis <= 1'b0; ctrl_flush <= 1'b0; end else if (bus_we_csr) begin ctrl_ovfl_dis <= bus_wdata[2]; ctrl_flush <= bus_wdata[1]; end always @(posedge clk or posedge rst) if (rst) ctrl_dif_flush <= 1'b0; else ctrl_dif_flush <= (ctrl_dif_flush | (bus_we_csr & bus_wdata[3])) & ~dif_empty; always @(posedge clk) begin df_ovfl_clr <= bus_we_csr & bus_wdata[31]; dif_ovfl_clr <= bus_we_csr & bus_wdata[15]; dif_unfl_clr <= bus_we_csr & bus_wdata[14]; dof_ovfl_clr <= bus_we_csr & bus_wdata[11]; end // Descriptor In FIFO write assign dif_wdata = bus_wdata[30:0]; assign dif_wex = bus_we_desc_in; // Descriptor Out FIFO read always @(posedge clk) dof_empty_r <= dof_empty; assign dof_re = bus_re_desc_out & ~dof_empty_r; // Input Data FIFO // --------------- // Instance fifo_sync_ram #( .DEPTH(512), .WIDTH(8) ) fifo_I ( .wr_data (df_wdata), .wr_ena (df_we), .wr_full (df_full), .rd_data (df_rdata), .rd_ena (df_re), .rd_empty (df_empty), .clk (clk), .rst (rst) ); // Level track assign df_level_move = { df_re & ~df_we, df_re ^ df_we }; always @(posedge clk) if (rst) df_level <= 0; else df_level <= df_level + { {9{df_level_move[1]}}, df_level_move[0] }; // Write safety and overflow track assign df_we = df_wex & ~df_full; assign df_ovfl_set = df_wex & df_full; always @(posedge clk) if (rst) df_ovfl <= 1'b0; else df_ovfl <= (df_ovfl & ~df_ovfl_clr) | df_ovfl_set; // Stream input assign df_wdata = stream_data; assign df_wex = stream_stb; // Descriptors FIFOs // ----------------- // In fifo_sync_shift #( .DEPTH(2), .WIDTH(31) ) dif_I ( .wr_data (dif_wdata), .wr_ena (dif_we), .wr_full (dif_full), .rd_data (dif_rdata), .rd_ena (dif_re), .rd_empty (dif_empty), .clk (clk), .rst (rst) ); assign dif_we = dif_wex & ~dif_full; assign dif_ovfl_set = dif_wex & dif_full; always @(posedge clk) if (rst) begin dif_ovfl <= 1'b0; dif_unfl <= 1'b0; end else begin dif_ovfl <= (dif_ovfl & ~dif_ovfl_clr) | dif_ovfl_set; dif_unfl <= (dif_unfl & ~dif_unfl_clr) | dif_unfl_set; end // Out fifo_sync_shift #( .DEPTH(2), .WIDTH(31) ) dof_I ( .wr_data (dof_wdata), .wr_ena (dof_we), .wr_full (dof_full), .rd_data (dof_rdata), .rd_ena (dof_re), .rd_empty (dof_empty), .clk (clk), .rst (rst) ); assign dof_we = dof_wex & ~dof_full; assign dof_ovfl_set = dof_wex & dof_full; always @(posedge clk) if (rst) dof_ovfl <= 1'b0; else dof_ovfl <= (dof_ovfl & ~dof_ovfl_clr) | dof_ovfl_set; // Control // ------- // Current state always @(posedge clk) if (rst) state <= ST_DISABLED; else state <= state_nxt; // Next state always @(*) begin // Default state_nxt = state; // Transitions case (state) ST_DISABLED: if (ctrl_active) state_nxt = ST_WAIT_BURST; ST_WAIT_BURST: if (~ctrl_active) state_nxt = ST_DISABLED; else if (lvl_enough | (ctrl_flush & ~lvl_zero)) state_nxt = ST_ISSUE; ST_ISSUE: if (mi_ready) state_nxt = ST_DATA; ST_DATA: if (mi_wack & mi_wlast) state_nxt = ST_DONE; ST_DONE: state_nxt = ST_WAIT_BURST; endcase end // Auto-disable always @(posedge clk) ctrl_disable_stb <= (ctrl_ovfl_dis & df_ovfl) | (ctrl_flush & lvl_zero & (state == ST_WAIT_BURST)); // Current descriptor assign cd_addr = dif_rdata[23:0]; assign cd_len = dif_rdata[30:24]; assign cd_valid = ~dif_empty; // Compare current level to descriptor size // (one cycle late but because ST_DONE gives us always one cycle // between last read and the next time this is used, we're good) always @(posedge clk) if (~cd_valid) lvl_cmp <= 0; else lvl_cmp <= { 4'b0000, cd_len } - { 1'b0, df_level }; assign lvl_enough = lvl_cmp[10]; // Detect zero levels always @(posedge clk) lvl_zero <= ~|df_level; // Ack descriptors assign dof_wdata = { mi_len, mi_addr }; assign dof_wex = mi_wack & mi_wlast; assign dif_re = (mi_wack & mi_wlast) | (ctrl_dif_flush & ~dif_empty); // "Underflows" assign dif_unfl_set = (state == ST_WAIT_BURST) & ~lvl_zero & dif_empty; // Memory interface // ---------------- // Valid assign mi_valid = (state == ST_ISSUE); // Address is straight from assign mi_addr = cd_addr; // Length depends on mode always @(posedge clk) if (state == ST_WAIT_BURST) begin if (lvl_enough) mi_len <= cd_len; else mi_len <= df_level[6:0] - 1; end // Data path assign mi_wdata = df_rdata; assign df_re = mi_wack; endmodule // ut_dma_wr