711299e67ccc6b10ca52863e8520b67e9e0ec085
[ls2.git] / examples / ls2.py
1 # Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
2 # Copyright (c) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
3 #
4 # Based on code from LambaConcept, from the gram example which is BSD-2-License
5 # https://github.com/jeanthom/gram/tree/master/examples
6 #
7 # Modifications for the Libre-SOC Project funded by NLnet and NGI POINTER
8 # under EU Grants 871528 and 957073, under the LGPLv3+ License
9
10 from nmigen import (Module, Elaboratable, DomainRenamer)
11 from nmigen.lib.cdc import ResetSynchronizer
12 from nmigen_soc import wishbone, memory
13 from nmigen_soc.memory import MemoryMap
14 from nmigen_stdio.serial import AsyncSerial
15
16 from lambdasoc.cpu.minerva import MinervaCPU
17 from lambdasoc.periph.intc import GenericInterruptController
18 from lambdasoc.periph.sram import SRAMPeripheral
19 from lambdasoc.periph.timer import TimerPeripheral
20 from lambdasoc.periph import Peripheral
21 from lambdasoc.soc.base import SoC
22 from soc.bus.uart_16550 import UART16550 # opencores 16550 uart
23
24 from gram.core import gramCore
25 from gram.phy.ecp5ddrphy import ECP5DDRPHY
26 from gram.modules import MT41K256M16
27 from gram.frontend.wishbone import gramWishbone
28
29 from nmigen_boards.versa_ecp5 import VersaECP5Platform
30 from nmigen_boards.ulx3s import ULX3S_85F_Platform
31 from nmigen_boards.arty_a7 import ArtyA7_100Platform
32 from nmigen_boards.test.blinky import Blinky
33
34 from crg import ECPIX5CRG
35
36 import sys
37 import os
38
39
40 class DDR3SoC(SoC, Elaboratable):
41 def __init__(self, *,
42 uart_pins, ddr_pins,
43 ddrphy_addr, dramcore_addr,
44 ddr_addr, fw_addr=0x0000_0000,
45 firmware=None,
46 clk_freq=40e6):
47
48 # set up wishbone bus arbiter and decoder. arbiter routes,
49 # decoder maps local-relative addressed satellites to global addresses
50 self._arbiter = wishbone.Arbiter(addr_width=30, data_width=32,
51 granularity=8,
52 features={"cti", "bte"})
53 self._decoder = wishbone.Decoder(addr_width=30, data_width=32,
54 granularity=8,
55 features={"cti", "bte"})
56
57 # default firmware name
58 if firmware is None:
59 firmware = "firmware/main.bin"
60
61 # set up clock request generator
62 self.crg = ECPIX5CRG()
63
64 # set up CPU, and interrupt interface
65 if False:
66 self.cpu = MinervaCPU(reset_address=0)
67 self._arbiter.add(self.cpu.ibus) # I-Cache Master
68 self._arbiter.add(self.cpu.dbus) # D-Cache Master. TODO JTAG master
69 self.intc = GenericInterruptController(width=len(self.cpu.ip))
70
71 # SRAM (but actually a ROM, for firmware), at address 0x0
72 if fw_addr is not None:
73 self.rom = SRAMPeripheral(size=4096, writable=False)
74 with open(firmware, "rb") as f:
75 words = iter(lambda: f.read(self.cpu.data_width // 8), b'')
76 bios = [int.from_bytes(w, self.cpu.byteorder) for w in words]
77 self.rom.init = bios
78 self._decoder.add(self.rom.bus, addr=fw_addr) # ROM at fw_addr
79
80 # SRAM (read-writeable BRAM)
81 self.ram = SRAMPeripheral(size=4096)
82 self._decoder.add(self.ram.bus, addr=0x8000000) # SRAM at 0x8000_000
83
84 # UART
85 self.uart = UART16550()
86 umap = MemoryMap(addr_width=7, data_width=8, name="uart_map")
87 #umap.add_resource(self._mem, name="mem", size=1<<5)
88 self.uart.bus.memory_map = umap
89
90 self._decoder.add(self.uart.bus, addr=0xc0002000) # 16550 UART address
91
92 # DRAM Module
93 self.ddrphy = DomainRenamer("dramsync")(ECP5DDRPHY(ddr_pins,
94 sys_clk_freq=100e6))
95 self._decoder.add(self.ddrphy.bus, addr=ddrphy_addr)
96
97 ddrmodule = MT41K256M16(clk_freq, "1:2") # match DDR3 ASIC P/N
98
99 drs = DomainRenamer("dramsync")
100 dramcore = gramCore(phy=self.ddrphy,
101 geom_settings=ddrmodule.geom_settings,
102 timing_settings=ddrmodule.timing_settings,
103 clk_freq=clk_freq)
104 self.dramcore = drs(dramcore)
105 self._decoder.add(self.dramcore.bus, addr=dramcore_addr)
106
107 # map the DRAM onto Wishbone
108 self.drambone = drs(gramWishbone(self.dramcore))
109 self._decoder.add(self.drambone.bus, addr=ddr_addr)
110
111 self.memory_map = self._decoder.bus.memory_map
112
113 self.clk_freq = clk_freq
114
115 def elaborate(self, platform):
116 m = Module()
117 comb = m.d.comb
118
119 # add the peripherals and clock-reset-generator
120 m.submodules.sysclk = self.crg
121
122 if hasattr(self, "rom"):
123 m.submodules.rom = self.rom
124 m.submodules.ram = self.ram
125 m.submodules.uart = self.uart
126 if False:
127 m.submodules.intc = self.intc
128 m.submodules.cpu = self.cpu
129 m.submodules.arbiter = self._arbiter
130 m.submodules.decoder = self._decoder
131 m.submodules.ddrphy = self.ddrphy
132 m.submodules.dramcore = self.dramcore
133 m.submodules.drambone = self.drambone
134
135 # add blinky lights so we know FPGA is alive
136 m.submodules.blinky = Blinky()
137
138 # connect the arbiter (of wishbone masters)
139 # to the decoder (addressing wishbone slaves)
140 comb += self._arbiter.bus.connect(self._decoder.bus)
141
142 if False:
143 # wire up the CPU interrupts
144 comb += self.cpu.ip.eq(self.intc.ip)
145
146 # add uart16550 verilog source. assumes a directory
147 # structure where ls2 has been checked out in a common
148 # subdirectory as https://github.com/freecores/uart16550
149 opencores_16550 = "../../uart16550/rtl/verilog"
150 pth = os.path.split(__file__)[0]
151 pth = os.path.join(pth, opencores_16550)
152 fname = os.path.abspath(pth)
153 print (fname)
154 self.uart.add_verilog_source(fname, platform)
155
156 return m
157
158
159 if __name__ == "__main__":
160
161 # create a platform selected from the toolchain. defaults to VERSA_ECP5
162 # only VERSA_ECP5 will work for now because of the DDR3 module
163 fpga = "versa_ecp5"
164 if len(sys.argv) >= 2:
165 fpga = sys.argv[1]
166 platform_kls = {'versa_ecp5': VersaECP5Platform,
167 'ulx3s': ULX3S_85F_Platform,
168 'arty_a7': ArtyA7_100Platform,
169 }[fpga]
170 toolchain = {'arty_a7': "yosys_nextpnr",
171 'versa_ecp5': 'Trellis',
172 'ulx3s': 'Trellis'
173 }.get(fpga, None)
174 platform = platform_kls(toolchain=toolchain)
175
176 # select a firmware file
177 firmware = None
178 fw_addr = None
179 if len(sys.argv) >= 3:
180 firmware = sys.argv[2]
181 fw_addr = 0x0000_0000
182
183 # get DDR and UART resource pins
184 ddr_pins = platform.request("ddr3", 0,
185 dir={"dq":"-", "dqs":"-"},
186 xdr={"clk":4, "a":4, "ba":4, "clk_en":4,
187 "odt":4, "ras":4, "cas":4, "we":4})
188 uart_pins = platform.request("uart", 0)
189
190 # set up the SOC
191 soc = DDR3SoC(ddrphy_addr=0xff000000, # DRAM firmware init base
192 dramcore_addr=0x80000000,
193 ddr_addr=0x10000000,
194 fw_addr=fw_addr,
195 ddr_pins=ddr_pins,
196 uart_pins=uart_pins,
197 firmware=firmware)
198
199 # build and upload it
200 platform.build(soc, do_program=True)