Skip to content

Commit

Permalink
DMA HW and test bring-up (#40)
Browse files Browse the repository at this point in the history
* snitch_cluster: Fix ID mismatch on wide input

* dma: Remove id module and fix reset value

* dma: Mark DMA transactions as modifiable

* testharness: Add DMA port into main memory

* runtime: Add stdlib and only remove startfiles

* sw/dma: Add DMA test

* Move DMA functions to stand-alone compilation unit

* Use FF macros for storage elements in DMA
  • Loading branch information
zarubaf authored Feb 15, 2021
1 parent 80c0ffe commit f510e68
Show file tree
Hide file tree
Showing 15 changed files with 391 additions and 258 deletions.
16 changes: 8 additions & 8 deletions hw/ip/snitch_cluster/src/snitch_cluster.sv
Original file line number Diff line number Diff line change
Expand Up @@ -264,14 +264,14 @@ module snitch_cluster
// Typedefs
// --------
typedef logic [PhysicalAddrWidth-1:0] addr_t;
typedef logic [NarrowDataWidth-1:0] data_t;
typedef logic [NarrowDataWidth/8-1:0] strb_t;
typedef logic [WideDataWidth-1:0] data_dma_t;
typedef logic [WideDataWidth/8-1:0] strb_dma_t;
typedef logic [NarrowIdWidthIn-1:0] id_mst_t;
typedef logic [IdWidthOut-1:0] id_slv_t;
typedef logic [WideIdWidthIn-1:0] id_dma_mst_t;
typedef logic [IdWidthDMAOut-1:0] id_dma_slv_t;
typedef logic [NarrowDataWidth-1:0] data_t;
typedef logic [NarrowDataWidth/8-1:0] strb_t;
typedef logic [WideDataWidth-1:0] data_dma_t;
typedef logic [WideDataWidth/8-1:0] strb_dma_t;
typedef logic [NarrowIdWidthIn-1:0] id_mst_t;
typedef logic [IdWidthOut-1:0] id_slv_t;
typedef logic [WideIdWidthIn-1:0] id_dma_mst_t;
typedef logic [IdWidthDMAOut-1:0] id_dma_slv_t;
typedef logic [UserWidth-1:0] user_t;

typedef logic [TCDMAddrWidth-1:0] tcdm_addr_t;
Expand Down
2 changes: 1 addition & 1 deletion hw/ip/snitch_cluster/src/snitch_cluster_wrapper.sv.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ package ${cfg['pkg_name']};
localparam int unsigned NrMasters = 3 + ${cfg['nr_hives']};
localparam int unsigned NarrowIdWidthOut = $clog2(NrMasters) + NarrowIdWidthIn;

localparam int unsigned NrDmaMasters = 1;
localparam int unsigned NrDmaMasters = 2;
localparam int unsigned WideIdWidthIn = ${cfg['dma_id_width_in']};
localparam int unsigned WideIdWidthOut = $clog2(NrDmaMasters) + WideIdWidthIn;

Expand Down
1 change: 0 additions & 1 deletion hw/ip/snitch_dma/Bender.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,5 @@ sources:
- src/axi_dma_error_handler.sv
- src/axi_dma_perf_counters.sv
- src/axi_dma_twod_ext.sv
- src/axi_dma_tc_snitch_fe_id_gen.sv
# Level 2:
- src/axi_dma_tc_snitch_fe.sv
4 changes: 2 additions & 2 deletions hw/ip/snitch_dma/src/axi_dma_error_handler.sv
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
// Thomas Benz <[email protected]>

// Sample implementation to report errors from the AXI bus.
// This module provides the adress of errors on a handshacked interface
// This module provides the address of errors on a handshaked interface

module axi_dma_error_handler #(
parameter int unsigned ADDR_WIDTH = -1,
Expand Down Expand Up @@ -193,4 +193,4 @@ module axi_dma_error_handler #(

end

endmodule : axi_dma_error_handler
endmodule
10 changes: 1 addition & 9 deletions hw/ip/snitch_dma/src/axi_dma_perf_counters.sv
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
// Thomas Benz <[email protected]>

// Sample implementation of performance counters.

module axi_dma_perf_counters #(
parameter int unsigned TRANSFER_ID_WIDTH = -1,
parameter int unsigned DATA_WIDTH = -1,
Expand Down Expand Up @@ -148,14 +147,7 @@ module axi_dma_perf_counters #(
if (dma_busy_i) dma_perf_d.dma_busy_cnt = dma_perf_q.dma_busy_cnt + 'h1;
end

always_ff @(posedge clk_i or negedge rst_ni) begin : proc_counter
if(~rst_ni) begin
dma_perf_q <= '0;
end else begin
dma_perf_q <= dma_perf_d;
end
end

`FF(dma_perf_q, dma_perf_d, 0);
assign dma_perf_o = dma_perf_q;

endmodule
253 changes: 122 additions & 131 deletions hw/ip/snitch_dma/src/axi_dma_tc_snitch_fe.sv
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
// Implements the tightly-coupled frontend. This module can directly be connected
// to an accelerator bus in the snitch system

`include "common_cells/registers.svh"

module axi_dma_tc_snitch_fe #(
parameter int unsigned AddrWidth = 0,
parameter int unsigned DataWidth = 0,
Expand Down Expand Up @@ -76,12 +78,11 @@ module axi_dma_tc_snitch_fe #(
//--------------------------------------
// Backend Instanciation
//--------------------------------------
logic backend_idle;
logic trans_complete;
logic backend_idle;
burst_req_t burst_req;
logic burst_req_valid;
logic burst_req_ready;
logic oned_trans_complete;
logic burst_req_valid;
logic burst_req_ready;
logic oned_trans_complete;

axi_dma_backend #(
.DataWidth ( DMADataWidth ),
Expand Down Expand Up @@ -162,16 +163,8 @@ module axi_dma_tc_snitch_fe #(
logic [31:0] next_id;
logic [31:0] completed_id;

axi_dma_tc_snitch_fe_id_gen #(
.ID_WIDTH ( 32 )
) i_axi_dma_tc_snitch_fe_id_gen (
.clk_i ( clk_i ),
.rst_ni ( rst_ni ),
.issue_i ( twod_req_valid && twod_req_ready ),
.retire_i ( oned_trans_complete && twod_req_last_realigned ),
.next_o ( next_id ),
.completed_o ( completed_id )
);
`FFL(next_id, next_id + 'h1, twod_req_valid & twod_req_ready, 0)
`FFL(completed_id, completed_id + 'h1, oned_trans_complete & twod_req_last_realigned, 0)

// dma is busy when it is not idle
assign dma_busy_o = next_id != completed_id;
Expand Down Expand Up @@ -230,6 +223,8 @@ module axi_dma_tc_snitch_fe #(
twod_req_d = twod_req_q;
twod_req_d.burst_src = axi_pkg::BURST_INCR;
twod_req_d.burst_dst = axi_pkg::BURST_INCR;
twod_req_d.cache_src = axi_pkg::CACHE_MODIFIABLE;
twod_req_d.cache_dst = axi_pkg::CACHE_MODIFIABLE;
twod_req_valid = 1'b0;
acc_qready_o = 1'b0;
acc_pdata_spill = '0;
Expand All @@ -241,125 +236,121 @@ module axi_dma_tc_snitch_fe #(
dma_op_name = "Invalid";

// decode
if (acc_qvalid_i == 1'b1) unique casez (acc_qdata_op_i)

// manipulate the source register
riscv_instr::DMSRC : begin
twod_req_d.src[31: 0] = acc_qdata_arga_i[31:0];
twod_req_d.src[AddrWidth-1:32] = acc_qdata_argb_i[AddrWidth-1-32: 0];
acc_qready_o = 1'b1;
is_dma_op = 1'b1;
dma_op_name = "DMSRC";
end

// manipulate the destination register
riscv_instr::DMDST : begin
twod_req_d.dst[31: 0] = acc_qdata_arga_i[31:0];
twod_req_d.dst[AddrWidth-1:32] = acc_qdata_argb_i[AddrWidth-1-32: 0];
acc_qready_o = 1'b1;
is_dma_op = 1'b1;
dma_op_name = "DMDST";
end

// start the DMA
riscv_instr::DMCPYI,
riscv_instr::DMCPY : begin
automatic logic [1:0] cfg;

// Parse the transfer parameters from the register or immediate.
unique casez (acc_qdata_op_i)
riscv_instr::DMCPYI : cfg = acc_qdata_op_i[24:20];
riscv_instr::DMCPY : cfg = acc_qdata_argb_i;
default:;
endcase
dma_op_name = "DMCPY";
is_dma_op = 1'b1;

twod_req_d.num_bytes = acc_qdata_arga_i;
twod_req_d.decouple_rw = cfg[0];
twod_req_d.is_twod = cfg[1];

// Perform the following sequence:
// 1. wait for acc response channel to be ready (pready)
// 2. request twod transfer (valid)
// 3. wait for twod transfer to be accepted (ready)
// 4. send acc response (pvalid)
// 5. acknowledge acc request (qready)
if (acc_pready_spill) begin
twod_req_valid = 1'b1;
if (twod_req_ready) begin
acc_pdata_spill.id = acc_qid_i;
acc_pdata_spill.data = next_id;
acc_pdata_spill.error = 1'b0;
acc_pvalid_spill = 1'b1;
acc_qready_o = twod_req_ready;
end
end
end

// status of the DMA
riscv_instr::DMSTATI,
riscv_instr::DMSTAT : begin
automatic logic [1:0] status;

// Parse the status index from the register or immediate.
unique casez (acc_qdata_op_i)
riscv_instr::DMSTATI : status = acc_qdata_op_i[24:20];
riscv_instr::DMSTAT : status = acc_qdata_argb_i;
default:;
endcase
dma_op_name = "DMSTAT";
is_dma_op = 1'b1;

// Compose the response.
acc_pdata_spill.id = acc_qid_i;
acc_pdata_spill.error = 1'b0;
case (status)
2'b00 : acc_pdata_spill.data = completed_id;
2'b01 : acc_pdata_spill.data = next_id;
2'b10 : acc_pdata_spill.data = {{{8'd63}{1'b0}}, dma_busy_o};
2'b11 : acc_pdata_spill.data = {{{8'd63}{1'b0}}, !twod_req_ready};
default:;
endcase

// Wait for acc response channel to become ready, then ack the
// request.
if (acc_pready_spill) begin
acc_pvalid_spill = 1'b1;
acc_qready_o = 1'b1;
end
end

// manipulate the strides
riscv_instr::DMSTR : begin
twod_req_d.stride_src = acc_qdata_arga_i;
twod_req_d.stride_dst = acc_qdata_argb_i;
acc_qready_o = 1'b1;
is_dma_op = 1'b1;
dma_op_name = "DMSTR";
end

// manipulate the strides
riscv_instr::DMREP : begin
twod_req_d.num_repetitions = acc_qdata_arga_i;
acc_qready_o = 1'b1;
is_dma_op = 1'b1;
dma_op_name = "DMREP";
end

default:;
endcase
if (acc_qvalid_i == 1'b1) begin
unique casez (acc_qdata_op_i)

// manipulate the source register
riscv_instr::DMSRC : begin
twod_req_d.src[31: 0] = acc_qdata_arga_i[31:0];
twod_req_d.src[AddrWidth-1:32] = acc_qdata_argb_i[AddrWidth-1-32: 0];
acc_qready_o = 1'b1;
is_dma_op = 1'b1;
dma_op_name = "DMSRC";
end

// manipulate the destination register
riscv_instr::DMDST : begin
twod_req_d.dst[31: 0] = acc_qdata_arga_i[31:0];
twod_req_d.dst[AddrWidth-1:32] = acc_qdata_argb_i[AddrWidth-1-32: 0];
acc_qready_o = 1'b1;
is_dma_op = 1'b1;
dma_op_name = "DMDST";
end

// start the DMA
riscv_instr::DMCPYI,
riscv_instr::DMCPY : begin
automatic logic [1:0] cfg;

// Parse the transfer parameters from the register or immediate.
unique casez (acc_qdata_op_i)
riscv_instr::DMCPYI : cfg = acc_qdata_op_i[24:20];
riscv_instr::DMCPY : cfg = acc_qdata_argb_i;
default:;
endcase
dma_op_name = "DMCPY";
is_dma_op = 1'b1;

twod_req_d.num_bytes = acc_qdata_arga_i;
twod_req_d.decouple_rw = cfg[0];
twod_req_d.is_twod = cfg[1];

// Perform the following sequence:
// 1. wait for acc response channel to be ready (pready)
// 2. request twod transfer (valid)
// 3. wait for twod transfer to be accepted (ready)
// 4. send acc response (pvalid)
// 5. acknowledge acc request (qready)
if (acc_pready_spill) begin
twod_req_valid = 1'b1;
if (twod_req_ready) begin
acc_pdata_spill.id = acc_qid_i;
acc_pdata_spill.data = next_id;
acc_pdata_spill.error = 1'b0;
acc_pvalid_spill = 1'b1;
acc_qready_o = twod_req_ready;
end
end
end

// status of the DMA
riscv_instr::DMSTATI,
riscv_instr::DMSTAT: begin
automatic logic [1:0] status;

// Parse the status index from the register or immediate.
unique casez (acc_qdata_op_i)
riscv_instr::DMSTATI: status = acc_qdata_op_i[24:20];
riscv_instr::DMSTAT: status = acc_qdata_argb_i;
default:;
endcase
dma_op_name = "DMSTAT";
is_dma_op = 1'b1;

// Compose the response.
acc_pdata_spill.id = acc_qid_i;
acc_pdata_spill.error = 1'b0;
case (status)
2'b00 : acc_pdata_spill.data = completed_id;
2'b01 : acc_pdata_spill.data = next_id;
2'b10 : acc_pdata_spill.data = {{{8'd63}{1'b0}}, dma_busy_o};
2'b11 : acc_pdata_spill.data = {{{8'd63}{1'b0}}, !twod_req_ready};
default:;
endcase

// Wait for acc response channel to become ready, then ack the
// request.
if (acc_pready_spill) begin
acc_pvalid_spill = 1'b1;
acc_qready_o = 1'b1;
end
end

// manipulate the strides
riscv_instr::DMSTR : begin
twod_req_d.stride_src = acc_qdata_arga_i;
twod_req_d.stride_dst = acc_qdata_argb_i;
acc_qready_o = 1'b1;
is_dma_op = 1'b1;
dma_op_name = "DMSTR";
end

// manipulate the strides
riscv_instr::DMREP : begin
twod_req_d.num_repetitions = acc_qdata_arga_i;
acc_qready_o = 1'b1;
is_dma_op = 1'b1;
dma_op_name = "DMREP";
end

default:;
endcase
end
end

//--------------------------------------
// State
//--------------------------------------
always_ff @(posedge clk_i or negedge rst_ni) begin : proc_modifiable_request
if(!rst_ni) begin
twod_req_q <= '0;
end else begin
twod_req_q <= twod_req_d;
end
end
`FF(twod_req_q, twod_req_d, '0)

endmodule : axi_dma_tc_snitch_fe
endmodule
Loading

0 comments on commit f510e68

Please sign in to comment.