| // Copyright 2023 Google LLC |
| // Copyright lowRISC contributors |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| /** |
| * DMA Engine |
| * |
| * The this module acts as a gasket for Antmicro's fastVDMA, modified to |
| * support TLUL as a front-end. |
| */ |
| |
| module dma |
| ( |
| input logic clk_i, |
| input logic rst_ni, |
| |
| // Reader Interface |
| output tlul_pkg::tl_h2d_t reader_tl_h_o, |
| input tlul_pkg::tl_d2h_t reader_tl_h_i, |
| |
| // Writer Interface |
| output tlul_pkg::tl_h2d_t writer_tl_h_o, |
| input tlul_pkg::tl_d2h_t writer_tl_h_i, |
| |
| // Control Interface |
| input tlul_pkg::tl_h2d_t tl_d_i, |
| output tlul_pkg::tl_d2h_t tl_d_o, |
| |
| // Interrupt Outputs |
| output logic intr_reader_done_o, |
| output logic intr_writer_done_o |
| |
| ); |
| |
| import top_pkg::*; |
| import tlul_pkg::*; |
| |
| logic [2:0] dma_control_d_opcode; |
| logic [2:0] dma_read_a_opcode; |
| logic [2:0] dma_write_a_opcode; |
| |
| logic a_ack; |
| logic wr_req; |
| logic addr_align_err; |
| logic meta_data_err; |
| logic tl_err; |
| logic error, error_int; |
| |
| assign a_ack = tl_d_i.a_valid & tl_d_o.a_ready; |
| assign wr_req = a_ack & ((tl_d_i.a_opcode == PutFullData) | (tl_d_i.a_opcode == PutPartialData)); |
| |
| assign reader_tl_h_o_pre.a_opcode = tlul_pkg::tl_a_op_e'(dma_read_a_opcode); |
| assign writer_tl_h_o_pre.a_opcode = tlul_pkg::tl_a_op_e'(dma_write_a_opcode); |
| |
| // Controller integrity checks |
| logic ctl_intg_err; |
| tlul_cmd_intg_chk ctl_chk( |
| .tl_i(tl_d_i), |
| .err_o(ctl_intg_err) |
| ); |
| |
| assign error_int = ctl_intg_err || addr_align_err || meta_data_err || tl_err; |
| always_ff @(posedge clk_i or negedge rst_ni) begin |
| if (!rst_ni) begin |
| error <= 1'b0; |
| end else if (a_ack) begin |
| error <= error_int; |
| end |
| end |
| |
| tlul_pkg::tl_d2h_t tl_d_o_pre; |
| assign tl_d_o_pre.d_opcode = tlul_pkg::tl_d_op_e'(dma_control_d_opcode); |
| assign tl_d_o_pre.d_user = '{default: '0}; |
| assign tl_d_o_pre.d_error = error; |
| |
| tlul_rsp_intg_gen #( |
| ) u_ctl_rsp_intg_gen ( |
| .tl_i(tl_d_o_pre), |
| .tl_o(tl_d_o) |
| ); |
| |
| |
| // Reader integrity checks |
| logic reader_intg_err; |
| tlul_rsp_intg_chk reader_chk( |
| .tl_i(reader_tl_h_i), |
| .err_o(reader_intg_err) |
| ); |
| |
| tlul_pkg::tl_h2d_t reader_tl_h_o_pre; |
| tlul_cmd_intg_gen #( |
| ) u_reader_cmd_intg_gen ( |
| .tl_i(reader_tl_h_o_pre), |
| .tl_o(reader_tl_h_o) |
| ); |
| |
| |
| // Writer integrity checks |
| logic writer_intg_err; |
| tlul_rsp_intg_chk writer_chk( |
| .tl_i(writer_tl_h_i), |
| .err_o(writer_intg_err) |
| ); |
| |
| tlul_pkg::tl_h2d_t writer_tl_h_o_pre; |
| tlul_cmd_intg_gen #( |
| ) u_writer_cmd_intg_gen ( |
| .tl_i(writer_tl_h_o_pre), |
| .tl_o(writer_tl_h_o) |
| ); |
| |
| |
| // Reader will never write any data |
| assign reader_tl_h_o_pre.a_user = '{ |
| default: '0, |
| instr_type: prim_mubi_pkg::MuBi4False, |
| data_intg: prim_secded_pkg::prim_secded_inv_39_32_enc('0) |
| }; |
| |
| assign writer_tl_h_o_pre.a_user = '{ |
| default: '0, |
| instr_type: prim_mubi_pkg::MuBi4False, |
| data_intg: '0 |
| }; |
| |
| |
| DMATop u_fastvdma ( |
| |
| .clock ( clk_i ), |
| .reset ( ~rst_ni ), |
| |
| // Control inputs |
| .io_control_a_opcode ( tl_d_i.a_opcode ), |
| .io_control_a_param ( tl_d_i.a_param ), |
| .io_control_a_size ( tl_d_i.a_size ), |
| .io_control_a_source ( tl_d_i.a_source ), |
| .io_control_a_address ( tl_d_i.a_address ), |
| .io_control_a_mask ( tl_d_i.a_mask ), |
| .io_control_a_data ( tl_d_i.a_data ), |
| .io_control_a_corrupt ( ), |
| .io_control_a_valid ( tl_d_i.a_valid ), |
| |
| .io_control_a_ready ( tl_d_o_pre.a_ready ), |
| |
| // Control outputs |
| .io_control_d_opcode ( dma_control_d_opcode ), |
| .io_control_d_param ( tl_d_o_pre.d_param ), |
| .io_control_d_size ( tl_d_o_pre.d_size ), |
| .io_control_d_source ( tl_d_o_pre.d_source ), |
| .io_control_d_sink ( tl_d_o_pre.d_sink ), |
| .io_control_d_denied ( ), |
| .io_control_d_data ( tl_d_o_pre.d_data ), |
| .io_control_d_corrupt ( ), |
| .io_control_d_valid ( tl_d_o_pre.d_valid ), |
| |
| .io_control_d_ready ( tl_d_i.d_ready ), |
| |
| |
| // Reader outputs |
| .io_read_a_opcode ( dma_read_a_opcode ), |
| .io_read_a_param ( reader_tl_h_o_pre.a_param ), |
| .io_read_a_size ( reader_tl_h_o_pre.a_size ), |
| .io_read_a_source ( reader_tl_h_o_pre.a_source ), |
| .io_read_a_address ( reader_tl_h_o_pre.a_address ), |
| .io_read_a_mask ( reader_tl_h_o_pre.a_mask ), |
| .io_read_a_data ( reader_tl_h_o_pre.a_data ), |
| .io_read_a_corrupt ( ), |
| .io_read_a_valid ( reader_tl_h_o_pre.a_valid ), |
| |
| .io_read_a_ready ( reader_tl_h_i.a_ready ), |
| |
| // Reader inputs |
| .io_read_d_opcode ( reader_tl_h_i.d_opcode ), |
| .io_read_d_param ( reader_tl_h_i.d_param ), |
| .io_read_d_size ( reader_tl_h_i.d_size ), |
| .io_read_d_source ( reader_tl_h_i.d_source ), |
| .io_read_d_sink ( reader_tl_h_i.d_sink ), |
| .io_read_d_denied ( ), |
| .io_read_d_data ( reader_tl_h_i.d_data ), |
| .io_read_d_corrupt ( ), |
| .io_read_d_valid ( reader_tl_h_i.d_valid ), |
| |
| .io_read_d_ready ( reader_tl_h_o_pre.d_ready ), |
| |
| |
| //// Writer outputs |
| .io_write_a_opcode ( dma_write_a_opcode ), |
| .io_write_a_param ( writer_tl_h_o_pre.a_param ), |
| .io_write_a_size ( writer_tl_h_o_pre.a_size ), |
| .io_write_a_source ( writer_tl_h_o_pre.a_source ), |
| .io_write_a_address ( writer_tl_h_o_pre.a_address ), |
| .io_write_a_mask ( writer_tl_h_o_pre.a_mask ), |
| .io_write_a_data ( writer_tl_h_o_pre.a_data ), |
| .io_write_a_corrupt ( ), |
| .io_write_a_valid ( writer_tl_h_o_pre.a_valid ), |
| |
| .io_write_a_ready ( writer_tl_h_i.a_ready ), |
| |
| // Writer inputs |
| .io_write_d_opcode ( writer_tl_h_i.d_opcode ), |
| .io_write_d_param ( writer_tl_h_i.d_param ), |
| .io_write_d_size ( writer_tl_h_i.d_size ), |
| .io_write_d_source ( writer_tl_h_i.d_source ), |
| .io_write_d_sink ( writer_tl_h_i.d_sink ), |
| .io_write_d_denied ( ), |
| .io_write_d_data ( writer_tl_h_i.d_data ), |
| .io_write_d_corrupt ( ), |
| .io_write_d_valid ( writer_tl_h_i.d_valid ), |
| |
| .io_write_d_ready ( writer_tl_h_o_pre.d_ready ), |
| |
| |
| // Other signals |
| .io_irq_readerDone ( intr_reader_done_o ), |
| .io_irq_writerDone ( intr_writer_done_o ), |
| .io_sync_readerSync ( 1'b0 ), |
| .io_sync_writerSync ( 1'b0 ) |
| ); |
| |
| // Explicitly throw controller error for comportability spec |
| // if writes aren't addr aligned. The DMA IP truncates the |
| // bottom two bits regardless. |
| always_comb begin |
| if (wr_req) begin |
| addr_align_err = |tl_d_i.a_address[1:0]; |
| end else begin |
| addr_align_err = 1'b0; |
| end |
| end |
| |
| assign meta_data_err = tl_a_user_chk(tl_d_i.a_user); |
| |
| tlul_err u_ctrl_err ( |
| .clk_i, |
| .rst_ni, |
| .tl_i (tl_d_i), |
| .err_o (tl_err) |
| ); |
| |
| endmodule |