|
| 1 | +/* |
| 2 | + * UMI to TLM-2 bridge. |
| 3 | + * |
| 4 | + * Copyright (c) 2024 Zero ASIC. |
| 5 | + * Written by Edgar E. Iglesias. |
| 6 | + * |
| 7 | + * SPDX-License-Identifier: MIT |
| 8 | + */ |
| 9 | + |
| 10 | +#ifndef UMI2TLM_BRIDGE_H__ |
| 11 | +#define UMI2TLM_BRIDGE_H__ |
| 12 | +#define SC_INCLUDE_DYNAMIC_PROCESSES |
| 13 | + |
| 14 | +#include "tlm-extensions/genattr.h" |
| 15 | +#include "tlm-bridges/umi.h" |
| 16 | + |
| 17 | +#undef D |
| 18 | +#define D(x) |
| 19 | + |
| 20 | +template <unsigned int DATA_WIDTH> |
| 21 | +class umi2tlm_bridge |
| 22 | +: public sc_core::sc_module |
| 23 | +{ |
| 24 | +public: |
| 25 | + tlm_utils::simple_initiator_socket<umi2tlm_bridge> socket; |
| 26 | + |
| 27 | + SC_HAS_PROCESS(umi2tlm_bridge); |
| 28 | + |
| 29 | + sc_in<bool> clk; |
| 30 | + sc_in<bool> rst; |
| 31 | + |
| 32 | + UMI_RX_PORT(req, DATA_WIDTH); |
| 33 | + UMI_TX_PORT(resp, DATA_WIDTH); |
| 34 | + |
| 35 | + umi2tlm_bridge(sc_core::sc_module_name name) : |
| 36 | + sc_module(name), |
| 37 | + socket("socket"), |
| 38 | + |
| 39 | + clk("clk"), |
| 40 | + rst("rst"), |
| 41 | + |
| 42 | + UMI_PORT_NAME(req), |
| 43 | + UMI_PORT_NAME(resp) |
| 44 | + { |
| 45 | + SC_THREAD(umi_thread); |
| 46 | + } |
| 47 | + |
| 48 | +private: |
| 49 | + uint8_t data[1024]; |
| 50 | + umi_fields f_tx; |
| 51 | + |
| 52 | + void send_resp(uint64_t sa, uint64_t da) { |
| 53 | + resp_cmd.write(f_tx.pack()); |
| 54 | + resp_srcaddr.write(sa); |
| 55 | + resp_dstaddr.write(da); |
| 56 | + |
| 57 | + resp_valid.write(1); |
| 58 | + do { |
| 59 | + wait(clk.posedge_event()); |
| 60 | + } while (!resp_ready.read()); |
| 61 | + resp_valid.write(0); |
| 62 | + wait(clk.posedge_event()); |
| 63 | + } |
| 64 | + |
| 65 | + void umi_thread() |
| 66 | + { |
| 67 | + genattr_extension *genattr = new genattr_extension(); |
| 68 | + tlm::tlm_generic_payload gp; |
| 69 | + sc_time delay(SC_ZERO_TIME); |
| 70 | + unsigned int pos; |
| 71 | + |
| 72 | + gp.set_data_ptr(reinterpret_cast<unsigned char*>(data)); |
| 73 | + gp.set_byte_enable_length(0); |
| 74 | + req_ready.write(1); |
| 75 | + |
| 76 | + while (true) { |
| 77 | + tlm::tlm_command gp_cmd; |
| 78 | + uint64_t sa, da; |
| 79 | + uint32_t cmd; |
| 80 | + unsigned int nbytes; |
| 81 | + unsigned int tlen; |
| 82 | + umi_fields f; |
| 83 | + sc_bv<DATA_WIDTH> data_bv; |
| 84 | + unsigned int i; |
| 85 | + |
| 86 | + wait(clk.posedge_event()); |
| 87 | + if (!req_valid.read()) { |
| 88 | + continue; |
| 89 | + } |
| 90 | + |
| 91 | + // Got a transaction. |
| 92 | + sa = req_srcaddr.read().to_uint64(); |
| 93 | + da = req_dstaddr.read().to_uint64(); |
| 94 | + cmd = req_cmd.read().to_uint64(); |
| 95 | + |
| 96 | + f.unpack(cmd); |
| 97 | + nbytes = (f.len + 1) << f.size; |
| 98 | + |
| 99 | + D(printf("UMI2TLM: %s addr=%lx -> %lx cmd=%x opc=%d size=%d len=%d %dB\n", |
| 100 | + umi_opc_str(f.opc), |
| 101 | + sa, da, cmd, f.opc, f.size, f.len, nbytes)); |
| 102 | + |
| 103 | + genattr->set_master_id(sa); |
| 104 | + gp.set_extension(genattr); |
| 105 | + gp.set_address(da); |
| 106 | + switch (f.opc) { |
| 107 | + case UMI_REQ_READ: gp_cmd = tlm::TLM_READ_COMMAND; break; |
| 108 | + case UMI_REQ_POSTED: /* Fall through. */ |
| 109 | + case UMI_REQ_WRITE: gp_cmd = tlm::TLM_WRITE_COMMAND; break; |
| 110 | + default: assert(0); |
| 111 | + }; |
| 112 | + gp.set_command(gp_cmd); |
| 113 | + gp.set_data_length(nbytes); |
| 114 | + gp.set_streaming_width(nbytes); |
| 115 | + assert(nbytes <= sizeof data); |
| 116 | + |
| 117 | + // Copy data. |
| 118 | + data_bv = req_data.read(); |
| 119 | + if (gp_cmd == tlm::TLM_WRITE_COMMAND) { |
| 120 | + for (i = 0; i < nbytes; i++) { |
| 121 | + data[i] = data_bv.range(i * 8 + 8 - 1, i * 8).to_uint(); |
| 122 | + } |
| 123 | + } |
| 124 | + socket->b_transport(gp, delay); |
| 125 | + |
| 126 | + // back-pressure until we're done. |
| 127 | + req_ready.write(0); |
| 128 | + wait(clk.posedge_event()); |
| 129 | + |
| 130 | + // Response. |
| 131 | + if (gp_cmd == tlm::TLM_READ_COMMAND) { |
| 132 | + // Write response is needed. |
| 133 | + f_tx = f; |
| 134 | + f_tx.opc = UMI_RESP_READ; |
| 135 | + f_tx.size = 0; |
| 136 | + |
| 137 | + /* Send multiple responses. */ |
| 138 | + pos = 0; |
| 139 | + do { |
| 140 | + tlen = std::min(DATA_WIDTH/8, nbytes - pos); |
| 141 | + f_tx.len = tlen - 1; |
| 142 | + |
| 143 | + for (i = 0; i < tlen; i++) { |
| 144 | + data_bv.range(i * 8 + 8 - 1, i * 8) = data[pos + i]; |
| 145 | + } |
| 146 | + resp_data.write(data_bv); |
| 147 | + |
| 148 | + f_tx.eof = 1; |
| 149 | + f_tx.eom = (pos + tlen) == nbytes; |
| 150 | + send_resp(da, sa); |
| 151 | + |
| 152 | + D(printf("UMI: %s %lx -> %lx pos=%d nbytes=%d eom=%d\n", |
| 153 | + umi_opc_str(f_tx.opc), |
| 154 | + da, sa, |
| 155 | + pos, nbytes, f_tx.eom)); |
| 156 | + |
| 157 | + // Advance state. |
| 158 | + pos += tlen; |
| 159 | + sa += tlen; |
| 160 | + da += tlen; |
| 161 | + } while (pos < nbytes); |
| 162 | + } else { |
| 163 | + pos = nbytes; |
| 164 | + if (f.opc == UMI_REQ_POSTED) { |
| 165 | + // No ACK should be sent. |
| 166 | + } else { |
| 167 | + assert(f.opc == UMI_REQ_WRITE); |
| 168 | + |
| 169 | + // Write response is needed. |
| 170 | + f_tx = f; |
| 171 | + f_tx.opc = UMI_RESP_WRITE; |
| 172 | + f_tx.eom = 1; |
| 173 | + send_resp(da, sa); |
| 174 | + // done |
| 175 | + } |
| 176 | + } |
| 177 | + req_ready.write(1); |
| 178 | + } |
| 179 | + } |
| 180 | +}; |
| 181 | +#undef D |
| 182 | +#endif |
0 commit comments