/ coreblocks / peripherals / bus_adapter.py
bus_adapter.py
  1  from typing import Protocol
  2  
  3  from amaranth import *
  4  from amaranth_types import HasElaborate
  5  
  6  from coreblocks.peripherals.wishbone import WishboneMaster
  7  from coreblocks.peripherals.axi_lite import AXILiteMaster
  8  
  9  from transactron import Method, def_method, TModule
 10  from transactron.lib import Serializer
 11  from transactron.utils.transactron_helpers import make_layout
 12  
 13  __all__ = ["BusMasterInterface", "WishboneMasterAdapter", "AXILiteMasterAdapter"]
 14  
 15  
 16  class BusParametersInterface(Protocol):
 17      """
 18      An interface for parameters of a common bus.
 19  
 20      Parameters
 21      ----------
 22      data_width : int
 23          An integer that describes the data width of a parametrized bus.
 24      addr_width : int
 25          An integer that describes the address width of a parametrized bus.
 26      granularity : int
 27          An integer that describes the granularity of accesses of a parametrized bus.
 28      """
 29  
 30      data_width: int
 31      addr_width: int
 32      granularity: int
 33  
 34  
 35  class BusMasterInterface(HasElaborate, Protocol):
 36      """
 37      An interface of a common bus.
 38  
 39      The bus interface is the preferred way to gain access to a specific bus.
 40      It simplifies interchangeability of buses on the core configuration level.
 41  
 42      Parameters
 43      ----------
 44      params : BusParametersInterface
 45          Parameters of the bus.
 46      request_read : Method
 47          A method that is used to send a read request to a bus.
 48      request_write : Method
 49          A method that is used to send a write request to a bus.
 50      get_read_response : Method
 51          A method that is used to receive a response from a bus for a previously sent read request.
 52      get_write_response : Method
 53          A method that is used to receive a response from a bus for a previously sent write request.
 54      """
 55  
 56      params: BusParametersInterface
 57      request_read: Method
 58      request_write: Method
 59      get_read_response: Method
 60      get_write_response: Method
 61  
 62  
 63  class CommonBusMasterMethodLayout:
 64      """
 65      Layouts of methods for a common bus master.
 66  
 67      Parameters
 68      ----------
 69      bus_params: BusParametersInterface
 70          Parameters used to generate common bus master methods layouts.
 71  
 72      Attributes
 73      ----------
 74      request_read_layout: Layout
 75          A layout for the `request_read` method of a common bus master.
 76  
 77      request_write_layout: Layout
 78          A layout for the `request_write` method of a common bus master.
 79  
 80      read_response_layout: Layout
 81          A layout for the `get_read_response` method of a common bus master.
 82  
 83      write_response_layout: Layout
 84          A layout for the `get_write_response` method of a common bus master.
 85      """
 86  
 87      def __init__(self, bus_params: BusParametersInterface):
 88          self.bus_params = bus_params
 89  
 90          self.request_read_layout = make_layout(
 91              ("addr", self.bus_params.addr_width),
 92              ("sel", self.bus_params.data_width // self.bus_params.granularity),
 93          )
 94  
 95          self.request_write_layout = make_layout(
 96              ("addr", self.bus_params.addr_width),
 97              ("data", self.bus_params.data_width),
 98              ("sel", self.bus_params.data_width // self.bus_params.granularity),
 99          )
100  
101          self.read_response_layout = make_layout(("data", self.bus_params.data_width), ("err", 1))
102  
103          self.write_response_layout = make_layout(("err", 1))
104  
105  
106  class WishboneMasterAdapter(Elaboratable, BusMasterInterface):
107      """
108      An adapter for Wishbone master.
109  
110      The adapter module is for use in places where BusMasterInterface is expected.
111  
112      Parameters
113      ----------
114      bus: WishboneMaster
115          Specific Wishbone master module which is to be adapted.
116  
117      Attributes
118      ----------
119      params: BusParametersInterface
120          Parameters of the bus.
121  
122      method_layouts: CommonBusMasterMethodLayout
123          Layouts of common bus master methods.
124  
125      request_read: Method
126          Transactional method for initiating a read request.
127          It is ready if the `request` method of the underlying Wishbone master is ready.
128          Input layout is `request_read_layout`.
129  
130      request_write: Method
131          Transactional method for initiating a write request.
132          It is ready if the `request` method of the underlying Wishbone master is ready.
133          Input layout is `request_write_layout`.
134  
135      get_read_response: Method
136          Transactional method for reading a response of a read action.
137          It is ready if the `result` method of the underlying Wishbone master is ready.
138          Output layout is `read_response_layout`.
139  
140      get_write_response: Method
141          Transactional method for reading a response of a write action.
142          It is ready if the `result` method of the underlying Wishbone master is ready.
143          Output layout is `write_response_layout`.
144      """
145  
146      def __init__(self, bus: WishboneMaster):
147          self.bus = bus
148          self.params = self.bus.wb_params
149  
150          self.method_layouts = CommonBusMasterMethodLayout(self.params)
151  
152          self.request_read = Method(i=self.method_layouts.request_read_layout)
153          self.request_write = Method(i=self.method_layouts.request_write_layout)
154          self.get_read_response = Method(o=self.method_layouts.read_response_layout)
155          self.get_write_response = Method(o=self.method_layouts.write_response_layout)
156  
157      def elaborate(self, platform):
158          m = TModule()
159  
160          bus_serializer = Serializer(
161              port_count=2, serialized_req_method=self.bus.request, serialized_resp_method=self.bus.result
162          )
163          m.submodules.bus_serializer = bus_serializer
164  
165          @def_method(m, self.request_read)
166          def _(arg):
167              we = C(0, unsigned(1))
168              data = C(0, unsigned(self.params.data_width))
169              bus_serializer.serialize_in[0](m, addr=arg.addr, data=data, we=we, sel=arg.sel)
170  
171          @def_method(m, self.request_write)
172          def _(arg):
173              we = C(1, unsigned(1))
174              bus_serializer.serialize_in[1](m, addr=arg.addr, data=arg.data, we=we, sel=arg.sel)
175  
176          @def_method(m, self.get_read_response)
177          def _():
178              res = bus_serializer.serialize_out[0](m)
179              return {"data": res.data, "err": res.err}
180  
181          @def_method(m, self.get_write_response)
182          def _():
183              res = bus_serializer.serialize_out[1](m)
184              return {"err": res.err}
185  
186          return m
187  
188  
189  class AXILiteMasterAdapter(Elaboratable, BusMasterInterface):
190      """
191      An adapter for AXI Lite master.
192  
193      The adapter module is for use in places where BusMasterInterface is expected.
194  
195      Parameters
196      ----------
197      bus: AXILiteMaster
198          Specific AXI Lite master module which is to be adapted.
199  
200      Attributes
201      ----------
202      params: BusParametersInterface
203          Parameters of the bus.
204  
205      method_layouts: CommonBusMasterMethodLayout
206          Layouts of common bus master methods.
207  
208      request_read: Method
209          Transactional method for initiating a read request.
210          It is ready if the `ra_request` method of the underlying AXI Lite master is ready.
211          Input layout is `request_read_layout`.
212  
213      request_write: Method
214          Transactional method for initiating a write request.
215          It is ready if the 'wa_request' and 'wd_request' methods of the underlying AXI Lite master are ready.
216          Input layout is `request_write_layout`.
217  
218      get_read_response: Method
219          Transactional method for reading a response of a read action.
220          It is ready if the `rd_response` method of the underlying AXI Lite master is ready.
221          Output layout is `read_response_layout`.
222  
223      get_write_response: Method
224          Transactional method for reading a response of a write action.
225          It is ready if the `wr_response` method of the underlying AXI Lite master is ready.
226          Output layout is `write_response_layout`.
227      """
228  
229      def __init__(self, bus: AXILiteMaster):
230          self.bus = bus
231          self.params = self.bus.axil_params
232  
233          self.method_layouts = CommonBusMasterMethodLayout(self.params)
234  
235          self.request_read = Method(i=self.method_layouts.request_read_layout)
236          self.request_write = Method(i=self.method_layouts.request_write_layout)
237          self.get_read_response = Method(o=self.method_layouts.read_response_layout)
238          self.get_write_response = Method(o=self.method_layouts.write_response_layout)
239  
240      def elaborate(self, platform):
241          m = TModule()
242  
243          @def_method(m, self.request_read)
244          def _(arg):
245              prot = C(0, unsigned(3))
246              self.bus.ra_request(m, addr=arg.addr, prot=prot)
247  
248          @def_method(m, self.request_write)
249          def _(arg):
250              prot = C(0, unsigned(3))
251              self.bus.wa_request(m, addr=arg.addr, prot=prot)
252              self.bus.wd_request(m, data=arg.data, strb=arg.sel)
253  
254          @def_method(m, self.get_read_response)
255          def _():
256              res = self.bus.rd_response(m)
257              err = res.resp != 0
258              return {"data": res.data, "err": err}
259  
260          @def_method(m, self.get_write_response)
261          def _():
262              res = self.bus.wr_response(m)
263              err = res.resp != 0
264              return {"err": err}
265  
266          return m