add external core verilog wrapper, ironically around Libre-SOC
[soc.git] / src / soc / bus / external_core.py
1 #!/usr/bin/env python3
2 #
3 # SPDX-License-Identifier: LGPLv3+
4 # Copyright (C) 2022 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
5 # Sponsored by NLnet and NGI POINTER under EU Grants 871528 and 957073
6 # Part of the Libre-SOC Project.
7 #
8 # this is a wrapper around the opencores verilog core16550 module
9
10 from nmigen import (Elaboratable, Cat, Module, Signal, ClockSignal, Instance,
11 ResetSignal)
12 from nmigen.cli import rtlil, verilog
13
14 from soc.debug.dmi import DMIInterface
15 from nmigen_soc.wishbone.bus import Interface
16 import os
17
18 __all__ = ["ExternalCore"]
19
20
21 class ExternalCore(Elaboratable):
22 """External Core verilog wrapper for microwatt and libre-soc
23 (actually, anything prepared to map to the Signals defined below)
24 remember to call ExternalCore.add_verilog_source
25 """
26
27 def __init__(self, ibus=None, dbus=None, features=None, name=None):
28
29 # set up the icache wishbone bus
30 if features is None:
31 features = frozenset(("stall",))
32 if ibus is None:
33 ibus = Interface(addr_width=32,
34 data_width=64,
35 features=features,
36 granularity=8,
37 name="core_ibus")
38 if dbus is None:
39 dbus = Interface(addr_width=32,
40 data_width=64,
41 features=features,
42 granularity=8,
43 name="core_dbus")
44 self.dmi = DMIInterface(name="dmi")
45 self.ibus = ibus
46 self.dbus = dbus
47
48 assert len(self.ibus.dat_r) == 64, "bus width must be 64"
49 assert len(self.dbus.dat_r) == 64, "bus width must be 64"
50
51 # IRQ for data buffer receive/xmit
52 self.irq = Signal()
53
54 # debug monitoring signals
55 self.nia = Signal(64)
56 self.nia_req = Signal()
57 self.msr = Signal(64)
58 self.msr_req = Signal()
59 self.ldst_addr = Signal(64)
60 self.ldst_req = Signal()
61
62 # alternative reset and termination indicator
63 self.alt_reset = Signal()
64 self.terminated_o = Signal()
65
66 @classmethod
67 def add_verilog_source(cls, verilog_src_dir, platform):
68 # add each of the verilog sources, needed for when doing platform.build
69 for fname in ['external_core_top.v',
70 ]:
71 # prepend the src directory to each filename, add its contents
72 fullname = os.path.join(verilog_src_dir, fname)
73 with open(fullname) as f:
74 platform.add_file(fullname, f)
75
76 def elaborate(self, platform):
77 m = Module()
78
79 # create definition of external core here, so that
80 # nmigen understands I/O directions (defined by i_ and o_ prefixes)
81 ibus, dbus, dmi = self.ibus, self.dbus, self.dmi
82 kwargs = {
83 # clock/reset signals
84 'i_clk_i': ClockSignal(),
85 'i_rst_i': ResetSignal(),
86 # DMI interface
87 'i_dmi_addr': dmi.addr_i,
88 'i_dmi_req': dmi.req_i,
89 'i_dmi_wr': dmi.we_i,
90 'i_dmi_din': dmi.din,
91 'o_dmi_dout': dmi.dout,
92 'o_dmi_ack': dmi.ack_o,
93 # debug/monitor signals
94 'o_nia': self.nia,
95 'o_nia_req': self.nia_req,
96 'o_msr': self.msr,
97 'o_msr_req': self.msr_req,
98 'o_ldst_addr': self.ldst_addr,
99 'o_ldst_req': self.ldst_req,
100 'i_alt_reset': self.alt_reset,
101 'o_terminated_out': self.terminated_o,
102 # wishbone instruction bus
103 'i_wishbone_insn_out.adr': ibus.adr,
104 'i_wishbone_insn_out.dat': ibus.dat_w,
105 'i_wishbone_insn_out.sel': ibus.sel,
106 'i_wishbone_insn_out.cyc': ibus.cyc,
107 'i_wishbone_insn_out.stb': ibus.stb,
108 'i_wishbone_insn_out.we': ibus.we,
109 'o_wishbone_insn_in.dat': ibus.dat_r,
110 'o_wishbone_insn_in.ack': ibus.ack,
111 'o_wishbone_insn_in.stall': ibus.stall,
112 # wishbone data bus
113 'i_wishbone_data_out.adr': dbus.adr,
114 'i_wishbone_data_out.dat': dbus.dat_w,
115 'i_wishbone_data_out.sel': dbus.sel,
116 'i_wishbone_data_out.cyc': dbus.cyc,
117 'i_wishbone_data_out.stb': dbus.stb,
118 'i_wishbone_data_out.we': dbus.we,
119 'o_wishbone_data_in.dat': dbus.dat_r,
120 'o_wishbone_data_in.ack': dbus.ack,
121 'o_wishbone_data_in.stall': dbus.stall,
122 # external interrupt request
123 'i_ext_irq': self.irq,
124 }
125 core = Instance("core_top", **kwargs)
126 m.submodules['core_top'] = core
127
128 return m
129
130
131 def create_ilang(dut, ports, test_name):
132 vl = rtlil.convert(dut, name=test_name, ports=ports)
133 with open("%s.il" % test_name, "w") as f:
134 f.write(vl)
135
136 def create_verilog(dut, ports, test_name):
137 vl = verilog.convert(dut, name=test_name, ports=ports)
138 with open("%s.v" % test_name, "w") as f:
139 f.write(vl)
140
141
142 if __name__ == "__main__":
143 core = ExternalCore(name="core")
144 create_ilang(core, [
145 core.ibus.cyc, core.ibus.stb, core.ibus.ack,
146 core.ibus.dat_r, core.ibus.dat_w, core.ibus.adr,
147 core.ibus.we, core.ibus.sel, core.ibus.stall,
148 core.dbus.cyc, core.dbus.stb, core.dbus.ack,
149 core.dbus.dat_r, core.dbus.dat_w, core.dbus.adr,
150 core.dbus.we, core.dbus.sel,
151 core.irq, core.alt_reset, core.terminated_o,
152 core.nia, core.nia_req,
153 core.msr, core.msr_req,
154 core.ldst_addr, core.ldst_req,
155 core.dmi.addr_i, core.dmi.req_i, core.dmi.we_i,
156 core.dmi.din, core.dmi.dout, core.dmi.ack_o,
157 ], "core_0")
158