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.
8 # this is a wrapper around the opencores verilog uart16550 module
10 from nmigen
import (Elaboratable
, Cat
, Module
, Signal
, ClockSignal
, Instance
,
13 from nmigen_soc
.wishbone
.bus
import Interface
14 from nmigen
.cli
import rtlil
, verilog
18 __all__
= ["UART16550"]
21 class UART16550(Elaboratable
):
22 """16550 UART from opencores, nmigen wrapper. remember to call
23 UART16550.add_verilog_source
26 def __init__(self
, bus
=None, features
=None, name
=None, data_width
=32,
29 # convention: give the name in the format "name_number"
30 self
.idx
= int(name
.split("_")[-1])
34 self
.data_width
= data_width
36 # set up the wishbone bus
38 features
= frozenset()
40 bus
= Interface(addr_width
=5,
41 data_width
=data_width
,
44 name
=name
+"_wb_%d" % self
.idx
)
46 assert len(self
.bus
.dat_r
) == data_width
, \
47 "bus width must be %d" % data_width
49 # IRQ for data buffer receive/xmit
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!
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())
74 platform
.add_file(t
.name
, t
)
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'
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
)
88 def elaborate(self
, platform
):
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
100 i_wb_dat_i
=bus
.dat_w
,
102 o_wb_dat_o
=bus
.dat_r
,
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
120 m
.submodules
['uart16550_%d' % self
.idx
] = uart
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
)
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
:
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
:
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
,
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