bceec5e29249b0048f0d0ca936b5b10243cc7505
[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):
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 self.irq = Signal()
51
52 # 9-pin UART signals (if anyone still remembers those...)
53 self.tx_o = Signal() # transmit
54 self.rx_i = Signal() # receive
55 self.rts_o = Signal() # ready to send
56 self.cts_i = Signal() # clear to send
57 self.dtr_o = Signal() # data terminal ready
58 self.dsr_i = Signal() # data send ready
59 self.ri_i = Signal() # can't even remember what this is!
60 self.dcd_i = Signal() # or this!
61
62 # pins resource
63 self.pins = pins
64
65 @classmethod
66 def add_verilog_source(cls, verilog_src_dir, platform):
67 # create a temp file containing "`define DATA_BUS_WIDTH_8"
68 t = tempfile.NamedTemporaryFile(delete=False, suffix=".v")
69 t.write("`define DATA_BUS_WIDTH_8\n".encode())
70 t.flush()
71 t.seek(0)
72 platform.add_file(t.name, t)
73
74 # add each of the verilog sources, needed for when doing platform.build
75 for fname in ['raminfr.v', 'uart_defines.v', 'uart_rfifo.v',
76 'uart_top.v', 'timescale.v', 'uart_receiver.v',
77 'uart_sync_flops.v', 'uart_transmitter.v',
78 'uart_debug_if.v', 'uart_regs.v',
79 'uart_tfifo.v', 'uart_wb.v'
80 ]:
81 # prepend the src directory to each filename, add its contents
82 fullname = os.path.join(verilog_src_dir, fname)
83 with open(fullname) as f:
84 platform.add_file(fullname, f)
85
86 def elaborate(self, platform):
87 m = Module()
88 comb = m.d.comb
89
90 # create definition of external verilog 16550 uart here, so that # nmigen understands I/O directions (defined by i_ and o_ prefixes)
91 idx, bus = self.idx, self.bus
92 uart = Instance("uart_top",
93 # clock/reset (use DomainRenamer if needed)
94 i_wb_clk_i=ClockSignal(),
95 i_wb_rst_i=ResetSignal(),
96 # wishbone bus signals
97 i_wb_adr_i=bus.adr,
98 i_wb_dat_i=bus.dat_w,
99 i_wb_sel_i=bus.sel,
100 o_wb_dat_o=bus.dat_r,
101 i_wb_we_i=bus.we,
102 i_wb_stb_i=bus.stb,
103 i_wb_cyc_i=bus.cyc,
104 o_wb_ack_o=bus.ack,
105 # interrupt line
106 o_int_o=self.irq,
107 # 9-pin RS232/UART signals
108 o_stx_pad_o=self.tx_o,
109 i_srx_pad_i=self.rx_i,
110 o_rts_pad_o=self.rts_o,
111 i_cts_pad_i=self.cts_i,
112 o_dtr_pad_o=self.dtr_o,
113 i_dsr_pad_i=self.dsr_i,
114 i_ri_pad_i=self.ri_i,
115 i_dcd_pad_i=self.dcd_i
116 );
117
118 m.submodules['uart16550_%d' % self.idx] = uart
119
120 if self.pins is not None:
121 comb += self.pins.tx.eq(self.tx_o)
122 comb += self.rx_i.eq(self.pins.rx)
123
124 return m
125
126
127 def create_ilang(dut, ports, test_name):
128 vl = rtlil.convert(dut, name=test_name, ports=ports)
129 with open("%s.il" % test_name, "w") as f:
130 f.write(vl)
131
132 def create_verilog(dut, ports, test_name):
133 vl = verilog.convert(dut, name=test_name, ports=ports)
134 with open("%s.v" % test_name, "w") as f:
135 f.write(vl)
136
137
138 if __name__ == "__main__":
139 uart = UART16550(name="uart_0", data_width=8)
140 create_ilang(uart, [uart.bus.cyc, uart.bus.stb, uart.bus.ack,
141 uart.bus.dat_r, uart.bus.dat_w, uart.bus.adr,
142 uart.bus.we, uart.bus.sel,
143 uart.irq,
144 uart.tx_o, uart.rx_i, uart.rts_o, uart.cts_i,
145 uart.dtr_o, uart.dsr_i, uart.ri_i, uart.dcd_i
146 ], "uart_0")
147