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