move IRQLine out because that makes soc dependent on LambdaSOC
[soc.git] / src / soc / bus / uart_16550.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 uart16550 module
9
10 from nmigen import (Elaboratable, Cat, Module, Signal, ClockSignal, Instance,
11 ResetSignal)
12
13 from nmigen_soc.wishbone.bus import Interface
14 from nmigen.cli import rtlil, verilog
15 import os
16 import tempfile
17
18 __all__ = ["UART16550"]
19
20
21 class UART16550(Elaboratable):
22 """16550 UART from opencores, nmigen wrapper. remember to call
23 UART16550.add_verilog_source
24 """
25
26 def __init__(self, bus=None, features=None, name=None, data_width=32,
27 pins=None, irq=None):
28 if name is not None:
29 # convention: give the name in the format "name_number"
30 self.idx = int(name.split("_")[-1])
31 else:
32 self.idx = 0
33 name = "uart_0"
34 self.data_width = data_width
35
36 # set up the wishbone bus
37 if features is None:
38 features = frozenset()
39 if bus is None:
40 bus = Interface(addr_width=5,
41 data_width=data_width,
42 features=features,
43 granularity=8,
44 name=name+"_wb_%d" % self.idx)
45 self.bus = bus
46 assert len(self.bus.dat_r) == data_width, \
47 "bus width must be %d" % data_width
48
49 # IRQ for data buffer receive/xmit
50 if irq is None:
51 irq = Signal()
52 self.irq = irq
53
54 # 9-pin UART signals (if anyone still remembers those...)
55 self.tx_o = Signal() # transmit
56 self.rx_i = Signal() # receive
57 self.rts_o = Signal() # ready to send
58 self.cts_i = Signal() # clear to send
59 self.dtr_o = Signal() # data terminal ready
60 self.dsr_i = Signal() # data send ready
61 self.ri_i = Signal() # can't even remember what this is!
62 self.dcd_i = Signal() # or this!
63
64 # pins resource
65 self.pins = pins
66
67 @classmethod
68 def add_verilog_source(cls, verilog_src_dir, platform):
69 # create a temp file containing "`define DATA_BUS_WIDTH_8"
70 t = tempfile.NamedTemporaryFile(delete=False, suffix=".v")
71 t.write("`define DATA_BUS_WIDTH_8\n".encode())
72 t.flush()
73 t.seek(0)
74 platform.add_file(t.name, t)
75
76 # add each of the verilog sources, needed for when doing platform.build
77 for fname in ['raminfr.v', 'uart_defines.v', 'uart_rfifo.v',
78 'uart_top.v', 'timescale.v', 'uart_receiver.v',
79 'uart_sync_flops.v', 'uart_transmitter.v',
80 'uart_debug_if.v', 'uart_regs.v',
81 'uart_tfifo.v', 'uart_wb.v'
82 ]:
83 # prepend the src directory to each filename, add its contents
84 fullname = os.path.join(verilog_src_dir, fname)
85 with open(fullname) as f:
86 platform.add_file(fullname, f)
87
88 def elaborate(self, platform):
89 m = Module()
90 comb = m.d.comb
91
92 # create definition of external verilog 16550 uart here, so that # nmigen understands I/O directions (defined by i_ and o_ prefixes)
93 idx, bus = self.idx, self.bus
94 uart = Instance("uart_top",
95 # clock/reset (use DomainRenamer if needed)
96 i_wb_clk_i=ClockSignal(),
97 i_wb_rst_i=ResetSignal(),
98 # wishbone bus signals
99 i_wb_adr_i=bus.adr,
100 i_wb_dat_i=bus.dat_w,
101 i_wb_sel_i=bus.sel,
102 o_wb_dat_o=bus.dat_r,
103 i_wb_we_i=bus.we,
104 i_wb_stb_i=bus.stb,
105 i_wb_cyc_i=bus.cyc,
106 o_wb_ack_o=bus.ack,
107 # interrupt line
108 o_int_o=self.irq,
109 # 9-pin RS232/UART signals
110 o_stx_pad_o=self.tx_o,
111 i_srx_pad_i=self.rx_i,
112 o_rts_pad_o=self.rts_o,
113 i_cts_pad_i=self.cts_i,
114 o_dtr_pad_o=self.dtr_o,
115 i_dsr_pad_i=self.dsr_i,
116 i_ri_pad_i=self.ri_i,
117 i_dcd_pad_i=self.dcd_i
118 );
119
120 m.submodules['uart16550_%d' % self.idx] = uart
121
122 if self.pins is not None:
123 comb += self.pins.tx.eq(self.tx_o)
124 comb += self.rx_i.eq(self.pins.rx)
125
126 return m
127
128
129 def create_ilang(dut, ports, test_name):
130 vl = rtlil.convert(dut, name=test_name, ports=ports)
131 with open("%s.il" % test_name, "w") as f:
132 f.write(vl)
133
134 def create_verilog(dut, ports, test_name):
135 vl = verilog.convert(dut, name=test_name, ports=ports)
136 with open("%s.v" % test_name, "w") as f:
137 f.write(vl)
138
139
140 if __name__ == "__main__":
141 uart = UART16550(name="uart_0", data_width=8)
142 create_ilang(uart, [uart.bus.cyc, uart.bus.stb, uart.bus.ack,
143 uart.bus.dat_r, uart.bus.dat_w, uart.bus.adr,
144 uart.bus.we, uart.bus.sel,
145 uart.irq,
146 uart.tx_o, uart.rx_i, uart.rts_o, uart.cts_i,
147 uart.dtr_o, uart.dsr_i, uart.ri_i, uart.dcd_i
148 ], "uart_0")
149