wb_async: Allow different feature fields for master/slave busses
[soc.git] / src / soc / bus / wb_async.py
1 # Copyright (C) 2022 Raptor Engineering, LLC <support@raptorengineering.com>
2 #
3 # Based partly on code from LibreSoC
4 #
5 # Modifications for the Libre-SOC Project funded by NLnet and NGI POINTER
6 # under EU Grants 871528 and 957073, under the LGPLv3+ License
7 #
8 # this is a wrapper around the Verilog Wishbone Components wb_async_reg module
9
10 from nmigen import (Elaboratable, Cat, Module, Signal, ClockSignal, Instance,
11 ResetSignal, Const)
12
13 from nmigen_soc.wishbone.bus import Interface
14 from nmigen_soc.memory import MemoryMap
15 from nmigen.utils import log2_int
16 from nmigen.cli import rtlil, verilog
17 from nmutil.byterev import byte_reverse
18 import os
19
20 __all__ = ["WBAsyncBridge"]
21
22
23 class WBAsyncBridge(Elaboratable):
24 """Verilog Wishbone Components wb_async_reg module, nmigen wrapper.
25 remember to call WBAsyncBridge.add_verilog_source
26 """
27
28 def __init__(self, master_bus=None, slave_bus=None, master_features=None,
29 slave_features=None, name=None,
30 address_width=30, data_width=32, granularity=8,
31 master_clock_domain=None, slave_clock_domain=None):
32 if name is not None:
33 # convention: give the name in the format "name_number"
34 self.idx = int(name.split("_")[-1])
35 else:
36 self.idx = 0
37 name = "wbasyncbridge_0"
38 self.address_width = address_width
39 self.data_width = data_width
40 self.granularity = granularity
41 self.dsize = log2_int(self.data_width//self.granularity)
42
43 # set up the clock domains
44 if master_clock_domain is None:
45 self.wb_mclk = ClockSignal()
46 self.wb_mrst = ResetSignal()
47 else:
48 self.wb_mclk = ClockSignal(master_clock_domain)
49 self.wb_mrst = ResetSignal(master_clock_domain)
50 if slave_clock_domain is None:
51 self.wb_sclk = ClockSignal()
52 self.wb_srst = ResetSignal()
53 else:
54 self.wb_sclk = ClockSignal(slave_clock_domain)
55 self.wb_srst = ResetSignal(slave_clock_domain)
56
57 # set up the wishbone busses
58 if master_features is None:
59 master_features = frozenset()
60 if slave_features is None:
61 slave_features = frozenset()
62 if master_bus is None:
63 master_bus = Interface(addr_width=self.address_width,
64 data_width=self.data_width,
65 features=master_features,
66 granularity=self.granularity,
67 name=name+"_wb_%d_master" % self.idx)
68 if slave_bus is None:
69 slave_bus = Interface(addr_width=self.address_width,
70 data_width=self.data_width,
71 features=slave_features,
72 granularity=self.granularity,
73 name=name+"_wb_%d_slave" % self.idx)
74 self.master_bus = master_bus
75 assert len(self.master_bus.dat_r) == data_width, \
76 "bus width must be %d" % data_width
77 self.slave_bus = slave_bus
78 assert len(self.slave_bus.dat_r) == data_width, \
79 "bus width must be %d" % data_width
80
81 @classmethod
82 def add_verilog_source(cls, verilog_src_dir, platform):
83 # add each of the verilog sources, needed for when doing platform.build
84 for fname in ['wb_async_reg.v']:
85 # prepend the src directory to each filename, add its contents
86 fullname = os.path.join(verilog_src_dir, fname)
87 with open(fullname) as f:
88 platform.add_file(fullname, f)
89
90 def elaborate(self, platform):
91 m = Module()
92 comb = m.d.comb
93 master_bus, slave_bus = self.master_bus, self.slave_bus
94 slave_ack = Signal()
95 slave_err = Signal()
96 slave_rty = Signal()
97
98 # create definition of external verilog bridge code here, so that
99 # nmigen understands I/O directions (defined by i_ and o_ prefixes)
100 idx = self.idx
101 wb_async_bridge = Instance("wb_async_reg",
102 # Parameters
103 p_ADDR_WIDTH=self.address_width,
104 p_DATA_WIDTH=self.data_width,
105 p_SELECT_WIDTH=self.granularity,
106
107 # Clocks/resets
108 i_wbm_clk=self.wb_mclk,
109 i_wbm_rst=self.wb_mrst,
110 i_wbs_clk=self.wb_sclk,
111 i_wbs_rst=self.wb_srst,
112
113 # Master Wishbone bus signals
114 i_wbm_adr_i=self.master_bus.adr,
115 i_wbm_dat_i=self.master_bus.dat_w,
116 o_wbm_dat_o=self.master_bus.dat_r,
117 i_wbm_we_i=self.master_bus.we,
118 i_wbm_sel_i=self.master_bus.sel,
119 i_wbm_stb_i=self.master_bus.stb,
120 i_wbm_cyc_i=self.master_bus.cyc,
121 o_wbm_ack_o=self.master_bus.ack,
122 #o_wbm_err=self.master_bus.err,
123 #o_wbm_rty_i=self.master_bus.rty,
124
125 # Slave Wishbone bus signals
126 o_wbs_adr_o=self.slave_bus.adr,
127 i_wbs_dat_i=self.slave_bus.dat_r,
128 o_wbs_dat_o=self.slave_bus.dat_w,
129 o_wbs_we_o=self.slave_bus.we,
130 o_wbs_sel_o=self.slave_bus.sel,
131 o_wbs_stb_o=self.slave_bus.stb,
132 o_wbs_cyc_o=self.slave_bus.cyc,
133 i_wbs_ack_i=slave_ack,
134 i_wbs_err_i=slave_err,
135 i_wbs_rty_i=slave_rty
136 );
137
138 # Synthesize STALL signal for master port
139 if hasattr(self.master_bus, "stall"):
140 comb += self.master_bus.stall.eq(self.master_bus.cyc & ~self.master_bus.ack)
141
142 # Convert incoming slave STALL signal to a format that the async bridge understands...
143 if hasattr(self.slave_bus, "stall"):
144 comb += slave_ack.eq(self.slave_bus.ack & ~self.slave_bus.stall)
145 else:
146 comb += slave_ack.eq(self.slave_bus.ack)
147
148 # Wire unused signals to 0
149 comb += slave_err.eq(0)
150 comb += slave_rty.eq(0)
151
152 m.submodules['wb_async_bridge_%d' % self.idx] = wb_async_bridge
153
154 return m
155
156 def ports(self):
157 return [self.master_bus.adr, self.master_bus.dat_w, self.master_bus.dat_r,
158 self.master_bus.we, self.master_bus.sel, self.master_bus.stb,
159 self.master_bus.cyc, self.master_bus.ack, self.master_bus.err,
160 self.master_bus.rty,
161 self.slave_bus.adr, self.slave_bus.dat_w, self.slave_bus.dat_r,
162 self.slave_bus.we, self.slave_bus.sel, self.slave_bus.stb,
163 self.slave_bus.cyc, self.slave_bus.ack, self.slave_bus.err,
164 self.slave_bus.rty
165 ]
166
167 def create_ilang(dut, ports, test_name):
168 vl = rtlil.convert(dut, name=test_name, ports=ports)
169 with open("%s.il" % test_name, "w") as f:
170 f.write(vl)
171
172 def create_verilog(dut, ports, test_name):
173 vl = verilog.convert(dut, name=test_name, ports=ports)
174 with open("%s.v" % test_name, "w") as f:
175 f.write(vl)
176
177
178 if __name__ == "__main__":
179 wbasyncbridge = WBAsyncBridge(name="wbasyncbridge_0", address_width=30, data_width=32, granularity=8)
180 create_ilang(wbasyncbridge, wbasyncbridge.ports(), "wbasyncbridge_0")