whitespace
[ls2.git] / src / ls2.py
1 # Copyright (c) 2020 LambdaConcept <contact@lambdaconcept.com>
2 # Copyright (c) 2021 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
3 # Copyright (C) 2022 Raptor Engineering, LLC <support@raptorengineering.com>
4 #
5 # Based on code from LambaConcept, from the gram example which is BSD-2-License
6 # https://github.com/jeanthom/gram/tree/master/examples
7 #
8 # Modifications for the Libre-SOC Project funded by NLnet and NGI POINTER
9 # under EU Grants 871528 and 957073, under the LGPLv3+ License
10
11 from nmigen import (Module, Elaboratable, DomainRenamer, Record,
12 Signal, Cat, Const, ClockSignal, ResetSignal)
13 from nmigen.build.dsl import Attrs
14 from nmigen.cli import verilog
15 from nmigen.lib.cdc import ResetSynchronizer
16 from nmigen_soc import wishbone, memory
17 from nmigen_soc.memory import MemoryMap
18 from nmigen.utils import log2_int
19
20 from nmigen_stdio.serial import AsyncSerial
21
22 # HyperRAM
23 from nmigen_boards.resources.memory import HyperRAMResource
24 from lambdasoc.periph.hyperram import HyperRAM, HyperRAMPads, HyperRAMPHY
25
26 from lambdasoc.periph.event import IRQLine
27 from lambdasoc.periph.intc import GenericInterruptController
28 from lambdasoc.periph.sram import SRAMPeripheral
29 from lambdasoc.periph.timer import TimerPeripheral
30 from lambdasoc.periph import Peripheral
31 from lambdasoc.soc.base import SoC
32 from soc.bus.uart_16550 import UART16550 # opencores 16550 uart
33 from soc.bus.tercel import Tercel # SPI XIP master
34 from soc.bus.opencores_ethmac import EthMAC # OpenCores 10/100 Ethernet MAC
35 from soc.bus.external_core import ExternalCore # external libresoc/microwatt
36 from soc.bus.wb_downconvert import WishboneDownConvert
37 from soc.bus.syscon import MicrowattSYSCON
38 from soc.interrupts.xics import XICS_ICP, XICS_ICS
39
40 # DDR3
41 from gram.common import (PhySettings, get_cl_cw, get_sys_latency,
42 get_sys_phases,)
43 from gram.core import gramCore
44 from gram.phy.ecp5ddrphy import ECP5DDRPHY
45 from gram.phy.fakephy import FakePHY, SDRAM_VERBOSE_STD, SDRAM_VERBOSE_DBG
46 from gram.modules import MT41K256M16, MT41K64M16
47 from gram.frontend.wishbone import gramWishbone
48
49 # SPI / Ethernet MAC
50 from nmigen.build import Resource
51 from nmigen.build import Subsignal
52 from nmigen.build import Pins
53
54 # Board (and simulation) platforms
55 from nmigen_boards.versa_ecp5 import VersaECP5Platform
56 from nmigen_boards.versa_ecp5 import VersaECP5Platform85 # custom board
57 from nmigen_boards.ulx3s import ULX3S_85F_Platform
58 from nmigen_boards.arty_a7 import ArtyA7_100Platform
59 from nmigen_boards.test.blinky import Blinky
60 from icarusversa import IcarusVersaPlatform
61 # Clock-Reset Generator (works for all ECP5 platforms)
62 from ecp5_crg import ECP5CRG
63 from arty_crg import ArtyA7CRG
64
65 import sys
66 import os
67
68 def sim_ddr3_settings(clk_freq=100e6):
69 tck = 2/(2*2*clk_freq)
70 nphases = 2
71 databits = 16
72 nranks = 1
73 addressbits = 14
74 bankbits = 3
75 cl, cwl = get_cl_cw("DDR3", tck)
76 cl_sys_latency = get_sys_latency(nphases, cl)
77 cwl_sys_latency = get_sys_latency(nphases, cwl)
78 rdcmdphase, rdphase = get_sys_phases(nphases, cl_sys_latency, cl)
79 wrcmdphase, wrphase = get_sys_phases(nphases, cwl_sys_latency, cwl)
80 return PhySettings(
81 phytype="ECP5DDRPHY",
82 memtype="DDR3",
83 databits=databits,
84 dfi_databits=4*databits,
85 nranks=nranks,
86 nphases=nphases,
87 rdphase=rdphase,
88 wrphase=wrphase,
89 rdcmdphase=rdcmdphase,
90 wrcmdphase=wrcmdphase,
91 cl=cl,
92 cwl=cwl,
93 read_latency=2 + cl_sys_latency + 2 + log2_int(4//nphases) + 4,
94 write_latency=cwl_sys_latency
95 )
96
97
98 class WB64to32Convert(Elaboratable):
99 """Microwatt IO wishbone slave 64->32 bits converter
100
101 For timing reasons, this adds a one cycle latch on the way both
102 in and out. This relaxes timing and routing pressure on the "main"
103 memory bus by moving all simple IOs to a slower 32-bit bus.
104
105 This implementation is rather dumb at the moment, no stash buffer,
106 so we stall whenever that latch is busy. This can be improved.
107 """
108 def __init__(self, master, slave):
109 self.master = master
110 self.slave = slave
111
112 def elaborate(self, platform):
113 m = Module()
114 comb, sync = m.d.comb, m.d.sync
115 master, slave = self.master, self.slave
116
117 has_top = Signal()
118 has_top_r = Signal()
119 has_bot = Signal()
120
121 with m.FSM() as fsm:
122 with m.State("IDLE"):
123 # Clear ACK (and has_top_r) in case it was set
124 sync += master.ack.eq(0)
125 sync += has_top_r.eq(0)
126
127 # Do we have a cycle ?
128 with m.If(master.cyc & master.stb):
129 # Stall master until we are done, we are't (yet) pipelining
130 # this, it's all slow IOs.
131 sync += master.stall.eq(1)
132
133 # Start cycle downstream
134 sync += slave.cyc.eq(1)
135 sync += slave.stb.eq(1)
136
137 # Do we have a top word and/or a bottom word ?
138 comb += has_top.eq(master.sel[4:].bool())
139 comb += has_bot.eq(master.sel[:4].bool())
140 # record the has_top flag for the next FSM state
141 sync += has_top_r.eq(has_top)
142
143 # Copy write enable to IO out, copy address as well,
144 # LSB is set later based on HI/LO
145 sync += slave.we.eq(master.we)
146 sync += slave.adr.eq(Cat(0, master.adr))
147
148 # If we have a bottom word, handle it first, otherwise
149 # send the top word down. XXX Split the actual mux out
150 # and only generate a control signal.
151 with m.If(has_bot):
152 with m.If(master.we):
153 sync += slave.dat_w.eq(master.dat_w[:32])
154 sync += slave.sel.eq(master.sel[:4])
155
156 # Wait for ack on BOTTOM half
157 m.next = "WAIT_ACK_BOT"
158
159 with m.Else():
160 with m.If(master.we):
161 sync += slave.dat_w.eq(master.dat_w[32:])
162 sync += slave.sel.eq(master.sel[4:])
163
164 # Bump LSB of address
165 sync += slave.adr[0].eq(1)
166
167 # Wait for ack on TOP half
168 m.next = "WAIT_ACK_TOP"
169
170
171 with m.State("WAIT_ACK_BOT"):
172 # If we aren't stalled by the device, clear stb
173 if hasattr(slave, "stall"):
174 with m.If(~slave.stall):
175 sync += slave.stb.eq(0)
176
177 # Handle ack
178 with m.If(slave.ack):
179 # If it's a read, latch the data
180 with m.If(~slave.we):
181 sync += master.dat_r[:32].eq(slave.dat_r)
182
183 # Do we have a "top" part as well ?
184 with m.If(has_top_r):
185 # Latch data & sel
186 with m.If(master.we):
187 sync += slave.dat_w.eq(master.dat_w[32:])
188 sync += slave.sel.eq(master.sel[4:])
189
190 # Bump address and set STB
191 sync += slave.adr[0].eq(1)
192 sync += slave.stb.eq(1)
193
194 # Wait for new ack
195 m.next = "WAIT_ACK_TOP"
196
197 with m.Else():
198 # We are done, ack up, clear cyc downstram
199 sync += slave.cyc.eq(0)
200 sync += slave.stb.eq(0)
201
202 # And ack & unstall upstream
203 sync += master.ack.eq(1)
204 if hasattr(master , "stall"):
205 sync += master.stall.eq(0)
206
207 # Wait for next one
208 m.next = "IDLE"
209
210 with m.State("WAIT_ACK_TOP"):
211 # If we aren't stalled by the device, clear stb
212 if hasattr(slave, "stall"):
213 with m.If(~slave.stall):
214 sync += slave.stb.eq(0)
215
216 # Handle ack
217 with m.If(slave.ack):
218 # If it's a read, latch the data
219 with m.If(~slave.we):
220 sync += master.dat_r[32:].eq(slave.dat_r)
221
222 # We are done, ack up, clear cyc downstram
223 sync += slave.cyc.eq(0)
224 sync += slave.stb.eq(0)
225
226 # And ack & unstall upstream
227 sync += master.ack.eq(1)
228 if hasattr(master, "stall"):
229 sync += master.stall.eq(0)
230
231 # Wait for next one
232 m.next = "IDLE"
233
234 return m
235
236
237 class DDR3SoC(SoC, Elaboratable):
238 def __init__(self, *,
239 fpga,
240 dram_cls=None,
241 uart_pins=None, spi_0_pins=None, ethmac_0_pins=None,
242 ddr_pins=None, ddrphy_addr=None,
243 dramcore_addr=None, ddr_addr=None,
244 fw_addr=0x0000_0000, firmware=None,
245 uart_addr=None, uart_irqno=0,
246 spi0_addr=None, spi0_cfg_addr=None,
247 eth0_cfg_addr=None, eth0_irqno=None,
248 hyperram_addr=None,
249 hyperram_pins=None,
250 xics_icp_addr=None, xics_ics_addr=None,
251 clk_freq=50e6,
252 add_cpu=True):
253
254 # wishbone routing is as follows:
255 #
256 # SoC
257 # +--+--+
258 # | |
259 # ibus dbus
260 # | |
261 # +--+--+
262 # |
263 # 64to32DownCvt
264 # |
265 # arbiter------------------------------------------+
266 # | |
267 # +---decoder----+--------+---------+-------+--------+ |
268 # | | | | | | | |
269 # uart XICS CSRs DRAM XIP SPI HyperRAM EthMAC
270
271 # set up wishbone bus arbiter and decoder. arbiter routes,
272 # decoder maps local-relative addressed satellites to global addresses
273 self._arbiter = wishbone.Arbiter(addr_width=30, data_width=32,
274 granularity=8,
275 features={"cti", "bte", "stall"})
276 self._decoder = wishbone.Decoder(addr_width=30, data_width=32,
277 granularity=8,
278 features={"cti", "bte", "stall"})
279
280 # default firmware name
281 if firmware is None:
282 firmware = "firmware/main.bin"
283
284 # set up clock request generator
285 pod_bits = 25
286 if fpga in ['versa_ecp5', 'versa_ecp5_85', 'isim', 'ulx3s',
287 'orangecrab']:
288 if fpga in ['isim']:
289 pod_bits = 6
290 self.crg = ECP5CRG(clk_freq, dram_clk_freq=None, pod_bits=pod_bits)
291 if fpga in ['arty_a7']:
292 self.crg = ArtyA7CRG(clk_freq)
293
294 # set up CPU, with 64-to-32-bit downconverters
295 if add_cpu:
296 self.cpu = ExternalCore(name="ext_core")
297 cvtdbus = wishbone.Interface(addr_width=30, data_width=32,
298 granularity=8, features={'stall'})
299 cvtibus = wishbone.Interface(addr_width=30, data_width=32,
300 granularity=8, features={'stall'})
301 self.dbusdowncvt = WB64to32Convert(self.cpu.dbus, cvtdbus)
302 self.ibusdowncvt = WB64to32Convert(self.cpu.ibus, cvtibus)
303 self._arbiter.add(cvtibus) # I-Cache Master
304 self._arbiter.add(cvtdbus) # D-Cache Master. TODO JTAG master
305 self.cvtibus = cvtibus
306 self.cvtdbus = cvtdbus
307
308 # CPU interrupt controller, needs stall to be added, also
309 # compat with wishbone.Interface
310 self.intc = GenericInterruptController(width=len(self.cpu.irq))
311 self.xics_icp = icp = XICS_ICP()
312 self.xics_ics = ics = XICS_ICS()
313 self.int_level_i = self.xics_ics.int_level_i
314
315 self.pbus = pbus = wishbone.Interface(name="xics_icp_bus",
316 addr_width=6, data_width=32,
317 granularity=8, features={'stall'})
318 self.sbus = sbus = wishbone.Interface(name="xics_ics_bus",
319 addr_width=10, data_width=32,
320 granularity=8, features={'stall'})
321 pmap = MemoryMap(addr_width=8, data_width=8, name="icp_map")
322 pbus.memory_map = pmap
323 self._decoder.add(pbus, addr=xics_icp_addr) # ICP addr
324
325 smap = MemoryMap(addr_width=12, data_width=8, name="ics_map")
326 sbus.memory_map = smap
327 self._decoder.add(sbus, addr=xics_ics_addr) # ICP addr
328
329
330 # SRAM (but actually a ROM, for firmware)
331 if fw_addr is not None:
332 print ("fw at address %x" % fw_addr)
333 sram_width = 32
334 self.bootmem = SRAMPeripheral(size=0x8000, data_width=sram_width,
335 writable=True)
336 if firmware is not None:
337 with open(firmware, "rb") as f:
338 words = iter(lambda: f.read(sram_width // 8), b'')
339 bios = [int.from_bytes(w, "little") for w in words]
340 self.bootmem.init = bios
341 self._decoder.add(self.bootmem.bus, addr=fw_addr) # ROM at fw_addr
342
343 # System Configuration info
344 # offset executable ELF payload at 6 megabyte offset (2<<20)
345 spi_offset = 2<<20 if (spi_0_pins is not None) else None
346 dram_offset = ddr_addr if (ddr_pins is not None) else None
347 self.syscon = MicrowattSYSCON(sys_clk_freq=clk_freq,
348 has_uart=(uart_pins is not None),
349 spi_offset=spi_offset,
350 dram_addr=dram_offset)
351 self._decoder.add(self.syscon.bus, addr=0xc0000000) # at 0xc000_0000
352
353 if False:
354 # SRAM (read-writeable BRAM)
355 self.ram = SRAMPeripheral(size=4096)
356 self._decoder.add(self.ram.bus, addr=0x8000000) # at 0x8000_0000
357
358 # UART at 0xC000_2000, convert 32-bit bus down to 8-bit in an odd way
359 if uart_pins is not None:
360 # sigh actual UART in microwatt is 8-bit
361 self.uart_irq = IRQLine()
362 self.uart = UART16550(data_width=8, pins=uart_pins,
363 features={'stall'},
364 irq=self.uart_irq)
365 # but (see soc.vhdl) 8-bit regs are addressed at 32-bit locations
366 # strictly speaking this is a nmigen-soc "sparse" arrangement
367 # which should be handled by MemoryMap, but needs investigation
368 cvtuartbus = wishbone.Interface(addr_width=5, data_width=32,
369 granularity=8,
370 features={'stall'})
371 umap = MemoryMap(addr_width=7, data_width=8, name="uart_map")
372 cvtuartbus.memory_map = umap
373 self._decoder.add(cvtuartbus, addr=uart_addr) # 16550 UART addr
374 self.cvtuartbus = cvtuartbus
375 self.intc.add_irq(self.uart.irq, index=uart_irqno)
376
377 # SDRAM module using opencores sdr_ctrl
378 """
379 class MT48LC16M16(SDRModule):
380 # geometry
381 nbanks = 4
382 nrows = 8192
383 ncols = 512
384 # timings
385 technology_timings = _TechnologyTimings(tREFI=64e6/8192,
386 tWTR=(2, None),
387 tCCD=(1, None),
388 tRRD=(None, 15))
389 speedgrade_timings = {"default": _SpeedgradeTimings(tRP=20,
390 tRCD=20,
391 tWR=15,
392 tRFC=(None, 66),
393 tFAW=None,
394 tRAS=44)}
395 """
396
397 # DRAM Module
398 if ddr_pins is not None: # or fpga == 'sim':
399 ddrmodule = dram_cls(clk_freq, "1:2") # match DDR3 ASIC P/N
400
401 # remap both the sync domain (wherever it occurs) and
402 # the sync2x domain. technically this should NOT be done.
403 # it's a bit of a mess. ok: this should be done only
404 # when dramsync===sync (and dramsync2x===sync2x)
405 drs = DomainRenamer({"sync": "dramsync",
406 "sync2x": "dramsync2x"})
407
408 # HOWEVER, when the ASyncBridge is deployed, the two domains
409 # must NOT be renamed, instead this used:
410 #drs = lambda x: x
411 # and then the ASyncBridge takes care of the two.
412 # but, back in ecp5_crg.py, when ASyncBridge is added,
413 # dram_clk_freq must be passed to ECP5CRG, which will call
414 # ECP5CRG.phase2_domain on your behalf, setting up the
415 # necessary dramsync2x which is needed for the xdr=4 IOpads
416
417 if fpga == 'sim':
418 self.ddrphy = FakePHY(module=ddrmodule,
419 settings=sim_ddr3_settings(clk_freq),
420 verbosity=SDRAM_VERBOSE_DBG,
421 clk_freq=clk_freq)
422 else:
423 self.ddrphy = drs(ECP5DDRPHY(ddr_pins, sys_clk_freq=clk_freq))
424 self._decoder.add(self.ddrphy.bus, addr=ddrphy_addr)
425
426 dramcore = gramCore(phy=self.ddrphy,
427 geom_settings=ddrmodule.geom_settings,
428 timing_settings=ddrmodule.timing_settings,
429 clk_freq=clk_freq)
430 if fpga == 'sim':
431 self.dramcore = dramcore
432 else:
433 self.dramcore = drs(dramcore)
434 self._decoder.add(self.dramcore.bus, addr=dramcore_addr)
435
436 # map the DRAM onto Wishbone, XXX use stall but set classic below
437 # XXX WHEN ADDING ASYNCBRIDGE IT IS THE **BRIDGE** THAT MUST
438 # XXX HAVE THE STALL SIGNAL, AND THE **BRIDGE** THAT MUST HAVE
439 # XXX stall=stb&~ack APPLIED
440 drambone = gramWishbone(dramcore, features={'stall'})
441 if fpga == 'sim':
442 self.drambone = drambone
443 else:
444 self.drambone = drs(drambone)
445 # XXX ADD THE ASYNCBRIDGE NOT THE DRAMBONE.BUS, THEN
446 # XXX ADD DRAMBONE.BUS TO ASYNCBRIDGE
447 self._decoder.add(self.drambone.bus, addr=ddr_addr)
448
449 # additional SRAM at address if DRAM is not also at 0x0
450 # (TODO, check Flash, and HyperRAM as well)
451 if (ddr_pins is None or ddr_addr != 0x0) and fw_addr != 0:
452 print ("SRAM 0x8000 at address 0x0")
453 sram_width = 32
454 self.sram = SRAMPeripheral(size=0x8000,
455 data_width=sram_width,
456 writable=True)
457 self._decoder.add(self.sram.bus, addr=0x0) # RAM at 0x0
458
459 # SPI controller
460 if spi_0_pins is not None and fpga in ['sim',
461 'isim',
462 'rcs_arctic_tern_bmc_card',
463 'versa_ecp5',
464 'versa_ecp5_85',
465 'arty_a7']:
466 # The Lattice ECP5 devices require special handling on the
467 # dedicated SPI clock line, which is shared with the internal
468 # SPI controller used for FPGA bitstream loading.
469 spi0_is_lattice_ecp5_clk = False
470 if fpga in ['versa_ecp5',
471 'versa_ecp5_85',
472 'rcs_arctic_tern_bmc_card',
473 'isim']:
474 spi0_is_lattice_ecp5_clk = True
475
476 # Tercel contains two independent Wishbone regions, a
477 # configuration region and the direct API access region,
478 # Set the SPI 0 access region to 16MB, as the FPGA
479 # bitstream Flash device is unlikely to be larger than this.
480 # The main SPI Flash (SPI 1) should be set to at
481 # least 28 bits (256MB) to allow the use of large 4BA devices.
482 self.spi0 = Tercel(data_width=32, spi_region_addr_width=24,
483 adr_offset=spi0_addr,
484 features={'stall'},
485 clk_freq=clk_freq,
486 pins=spi_0_pins,
487 lattice_ecp5_usrmclk=spi0_is_lattice_ecp5_clk)
488 self._decoder.add(self.spi0.bus, addr=spi0_addr)
489 self._decoder.add(self.spi0.cfg_bus, addr=spi0_cfg_addr)
490
491 # Ethernet MAC
492 if ethmac_0_pins is not None and fpga in ['versa_ecp5',
493 'versa_ecp5_85',
494 'isim']:
495 self.eth_irq = IRQLine()
496 # The OpenCores Ethernet MAC contains two independent Wishbone
497 # interfaces, a slave (configuration) interface and a master (DMA)
498 # interface.
499 self.eth0 = EthMAC(pins=ethmac_0_pins, irq=self.eth_irq)
500 self._arbiter.add(self.eth0.master_bus)
501 self._decoder.add(self.eth0.slave_bus, addr=eth0_cfg_addr)
502 self.intc.add_irq(self.eth0.irq, index=eth0_irqno)
503
504 # HyperRAM modules *plural*. Assumes using a Quad PMOD by Piotr
505 # Esden, sold by 1bitsquared, only doing one CS_N enable at the
506 # moment
507 if hyperram_pins is not None:
508 self.hyperram = HyperRAM(io=hyperram_pins, phy_kls=HyperRAMPHY,
509 features={'stall'},
510 latency=7) # Winbond W956D8MBYA
511 self._decoder.add(self.hyperram.bus, addr=hyperram_addr)
512
513 self.memory_map = self._decoder.bus.memory_map
514
515 self.clk_freq = clk_freq
516 self.fpga = fpga
517
518 def elaborate(self, platform):
519 m = Module()
520 comb = m.d.comb
521
522 # add the peripherals and clock-reset-generator
523 if platform is not None and hasattr(self, "crg"):
524 m.submodules.sysclk = self.crg
525
526 if hasattr(self, "sram"):
527 m.submodules.sram = self.sram
528 if hasattr(self, "bootmem"):
529 m.submodules.bootmem = self.bootmem
530 m.submodules.syscon = self.syscon
531 if hasattr(self, "ram"):
532 m.submodules.ram = self.ram
533 if hasattr(self, "uart"):
534 m.submodules.uart = self.uart
535 comb += self.uart.cts_i.eq(1)
536 comb += self.uart.dsr_i.eq(1)
537 comb += self.uart.ri_i.eq(0)
538 comb += self.uart.dcd_i.eq(1)
539 # sigh connect up the wishbone bus manually to deal with
540 # the mis-match on the data. nmigen-soc "sparse" MemoryMap
541 # should be able to deal with this. TODO, investigate
542 uartbus = self.uart.bus
543 comb += uartbus.adr.eq(self.cvtuartbus.adr)
544 comb += uartbus.stb.eq(self.cvtuartbus.stb)
545 comb += uartbus.cyc.eq(self.cvtuartbus.cyc)
546 comb += uartbus.sel.eq(self.cvtuartbus.sel)
547 comb += uartbus.we.eq(self.cvtuartbus.we)
548 comb += uartbus.dat_w.eq(self.cvtuartbus.dat_w) # drops 8..31
549 comb += self.cvtuartbus.dat_r.eq(uartbus.dat_r) # drops 8..31
550 comb += self.cvtuartbus.ack.eq(uartbus.ack)
551 # aaand with the WB4-pipeline-to-WB3-classic mismatch, sigh
552 comb += uartbus.stall.eq(uartbus.cyc & ~uartbus.ack)
553 comb += self.cvtuartbus.stall.eq(uartbus.stall)
554 if hasattr(self, "cpu"):
555 m.submodules.intc = self.intc
556 m.submodules.extcore = self.cpu
557 m.submodules.dbuscvt = self.dbusdowncvt
558 m.submodules.ibuscvt = self.ibusdowncvt
559 # create stall sigs, assume wishbone classic
560 #ibus, dbus = self.cvtibus, self.cvtdbus
561 #comb += ibus.stall.eq(ibus.stb & ~ibus.ack)
562 #comb += dbus.stall.eq(dbus.stb & ~dbus.ack)
563
564 m.submodules.arbiter = self._arbiter
565 m.submodules.decoder = self._decoder
566 if hasattr(self, "ddrphy"):
567 m.submodules.ddrphy = self.ddrphy
568 m.submodules.dramcore = self.dramcore
569 m.submodules.drambone = drambone = self.drambone
570 # grrr, same problem with drambone: not WB4-pipe compliant
571 # XXX TAKE THIS OUT, REPLACE WITH ASYNCBRIDGE HAVING
572 # XXX asyncbridge.bus.stall.eq(asyncbridge.bus.cyc & ...)
573 comb += drambone.bus.stall.eq(drambone.bus.cyc & ~drambone.bus.ack)
574
575 # add hyperram module
576 if hasattr(self, "hyperram"):
577 m.submodules.hyperram = hyperram = self.hyperram
578 # grrr, same problem with hyperram: not WB4-pipe compliant
579 comb += hyperram.bus.stall.eq(hyperram.bus.cyc & ~hyperram.bus.ack)
580 # set 3 top CSn lines to zero for now
581 if self.fpga == 'arty_a7':
582 comb += hyperram.phy.rst_n.eq(ResetSignal())
583
584 # add blinky lights so we know FPGA is alive
585 if platform is not None:
586 m.submodules.blinky = Blinky()
587
588 # connect the arbiter (of wishbone masters)
589 # to the decoder (addressing wishbone slaves)
590 comb += self._arbiter.bus.connect(self._decoder.bus)
591
592 if hasattr(self, "cpu"):
593 m.submodules.xics_icp = icp = self.xics_icp
594 m.submodules.xics_ics = ics = self.xics_ics
595 comb += icp.ics_i.eq(ics.icp_o) # connect ICS to ICP
596 comb += self.cpu.irq.eq(icp.core_irq_o) # connect ICP to core
597
598 # wire up the CPU interrupts from the GenericInterrupt
599 comb += self.int_level_i.eq(self.intc.ip)
600
601 # grrr
602 comb += self.pbus.stall.eq(self.pbus.cyc & ~self.pbus.ack)
603 comb += self.sbus.stall.eq(self.sbus.cyc & ~self.sbus.ack)
604
605 # and also wire up make_wb_layout() to wishbone.Interface.
606 # really, XICS_ICS and XICS_ICP both need to be converted
607 # to use wishbone.Interface and this all goes
608 comb += icp.bus.adr.eq(self.pbus.adr)
609 comb += icp.bus.dat_w.eq(self.pbus.dat_w)
610 comb += icp.bus.cyc.eq(self.pbus.cyc)
611 comb += icp.bus.stb.eq(self.pbus.stb)
612 comb += icp.bus.we.eq(self.pbus.we)
613 comb += self.pbus.ack.eq(icp.bus.ack)
614 comb += self.pbus.dat_r.eq(icp.bus.dat_r)
615 comb += ics.bus.adr.eq(self.sbus.adr)
616 comb += ics.bus.dat_w.eq(self.sbus.dat_w)
617 comb += ics.bus.cyc.eq(self.sbus.cyc)
618 comb += ics.bus.stb.eq(self.sbus.stb)
619 comb += ics.bus.we.eq(self.sbus.we)
620 comb += self.sbus.ack.eq(ics.bus.ack)
621 comb += self.sbus.dat_r.eq(ics.bus.dat_r)
622
623 if platform is None:
624 return m
625
626 # add uart16550 verilog source. assumes a directory
627 # structure where ls2 has been checked out in a common
628 # subdirectory as:
629 # git clone https://github.com/freecores/uart16550
630 opencores_16550 = "../../uart16550/rtl/verilog"
631 pth = os.path.split(__file__)[0]
632 pth = os.path.join(pth, opencores_16550)
633 fname = os.path.abspath(pth)
634 print (fname)
635 self.uart.add_verilog_source(fname, platform)
636
637 if hasattr(self, "spi0"):
638 # add spi submodule
639 m.submodules.spi0 = spi = self.spi0
640 # gonna drive me nuts, this.
641 comb += spi.bus.stall.eq(spi.bus.cyc & ~spi.bus.ack)
642 comb += spi.cfg_bus.stall.eq(spi.cfg_bus.cyc & ~spi.cfg_bus.ack)
643
644 # add Tercel verilog source. assumes a directory structure where
645 # microwatt has been checked out in a common subdirectory with:
646 # git clone https://git.libre-soc.org/git/microwatt.git tercel-qspi
647 # git checkout 882ace781e4
648 raptor_tercel = "../../tercel-qspi/tercel"
649 pth = os.path.split(__file__)[0]
650 pth = os.path.join(pth, raptor_tercel)
651 fname = os.path.abspath(pth)
652 print (fname)
653 self.spi0.add_verilog_source(fname, platform)
654
655 if hasattr(self, "eth0"):
656 # add ethernet submodule
657 m.submodules.eth0 = ethmac = self.eth0
658
659 # add EthMAC verilog source. assumes a directory
660 # structure where the opencores ethmac has been checked out
661 # in a common subdirectory as:
662 # git clone https://github.com/freecores/ethmac
663 opencores_ethmac = "../../ethmac/rtl/verilog"
664 pth = os.path.split(__file__)[0]
665 pth = os.path.join(pth, opencores_ethmac)
666 fname = os.path.abspath(pth)
667 print (fname)
668 self.eth0.add_verilog_source(fname, platform)
669
670 # add the main core
671 pth = os.path.split(__file__)[0]
672 pth = os.path.join(pth, '../external_core_top.v')
673 fname = os.path.abspath(pth)
674 with open(fname) as f:
675 platform.add_file(fname, f)
676
677 return m
678
679 def ports(self):
680 # puzzlingly the only IO ports needed are peripheral pins,
681 # and at the moment that's just UART tx/rx.
682 ports = []
683 ports += [self.uart.tx_o, self.uart.rx_i]
684 if hasattr(self, "hyperram"):
685 ports += list(self.hyperram.ports())
686 if hasattr(self, "ddrphy"):
687 if hasattr(self.ddrphy, "pads"): # real PHY
688 ports += list(self.ddrphy.pads.fields.values())
689 else: # FakePHY, get at the dfii pads, stops deletion of nets
690 for phase in self.dramcore.dfii.master.phases:
691 print ("dfi master", phase)
692 ports += list(phase.fields.values())
693 for phase in self.dramcore.dfii.slave.phases:
694 print ("dfi master", phase)
695 ports += list(phase.fields.values())
696 for phase in self.dramcore.dfii._inti.phases:
697 print ("dfi master", phase)
698 ports += list(phase.fields.values())
699 ports += [ClockSignal(), ResetSignal()]
700 return ports
701
702 def build_platform(fpga, firmware):
703
704 # create a platform selected from the toolchain.
705 platform_kls = {'versa_ecp5': VersaECP5Platform,
706 'versa_ecp5_85': VersaECP5Platform85,
707 'ulx3s': ULX3S_85F_Platform,
708 'arty_a7': ArtyA7_100Platform,
709 'isim': IcarusVersaPlatform,
710 'sim': None,
711 }[fpga]
712 toolchain = {'arty_a7': "yosys_nextpnr",
713 'versa_ecp5': 'Trellis',
714 'versa_ecp5_85': 'Trellis',
715 'isim': 'Trellis',
716 'ulx3s': 'Trellis',
717 'sim': None,
718 }.get(fpga, None)
719 dram_cls = {'arty_a7': None,
720 'versa_ecp5': MT41K64M16,
721 'versa_ecp5_85': MT41K64M16,
722 #'versa_ecp5': MT41K256M16,
723 'ulx3s': None,
724 'sim': MT41K256M16,
725 'isim': MT41K64M16,
726 }.get(fpga, None)
727 if platform_kls is not None:
728 platform = platform_kls(toolchain=toolchain)
729 if fpga == 'versa_ecp5_85':
730 platform.speed = "7" # HACK. speed grade 7, sigh
731 else:
732 platform = None
733
734 print ("platform", fpga, firmware, platform)
735
736 # set clock frequency
737 clk_freq = 70e6
738 if fpga == 'sim':
739 clk_freq = 100e6
740 if fpga == 'isim':
741 clk_freq = 55e6 # below 50 mhz, stops DRAM being enabled
742 if fpga == 'versa_ecp5':
743 clk_freq = 50e6 # crank right down to test hyperram
744 if fpga == 'versa_ecp5_85':
745 # 50MHz works. 100MHz works. 55MHz does NOT work.
746 # Stick with multiples of 50MHz...
747 clk_freq = 50e6
748 if fpga == 'arty_a7':
749 clk_freq = 50e6
750 if fpga == 'ulx3s':
751 clk_freq = 40.0e6
752
753 # select a firmware address
754 fw_addr = None
755 if firmware is not None:
756 fw_addr = 0xff00_0000 # firmware at HI address, now
757
758 print ("fpga", fpga, "firmware", firmware)
759
760 # get UART resource pins
761 if platform is not None:
762 uart_pins = platform.request("uart", 0)
763 else:
764 uart_pins = Record([('tx', 1), ('rx', 1)], name="uart_0")
765
766 # get DDR resource pins, disable if clock frequency is below 50 mhz for now
767 ddr_pins = None
768 if (clk_freq >= 50e6 and platform is not None and
769 fpga in ['versa_ecp5', 'versa_ecp5_85', 'arty_a7', 'isim']):
770 ddr_pins = platform.request("ddr3", 0,
771 dir={"dq":"-", "dqs":"-"},
772 xdr={"rst": 1, "clk":4, "a":4,
773 "ba":4, "clk_en":4,
774 "odt":4, "ras":4, "cas":4, "we":4,
775 "cs": 4})
776
777 # Get SPI resource pins
778 spi_0_pins = None
779 if platform is not None and \
780 fpga in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
781 # Override here to get FlashResource out of the way and enable Tercel
782 # direct access to the SPI flash.
783 # each pin needs a separate direction control
784 spi_0_ios = [
785 Resource("spi_0", 0,
786 Subsignal("dq0", Pins("W2", dir="io")),
787 Subsignal("dq1", Pins("V2", dir="io")),
788 Subsignal("dq2", Pins("Y2", dir="io")),
789 Subsignal("dq3", Pins("W1", dir="io")),
790 Subsignal("cs_n", Pins("R2", dir="o")),
791 Attrs(PULLMODE="NONE", DRIVE="4", IO_TYPE="LVCMOS33"))
792 ]
793 platform.add_resources(spi_0_ios)
794 spi_0_pins = platform.request("spi_0", 0, dir={"cs_n":"o"},
795 xdr={"dq0":1, "dq1": 1,
796 "dq2":1, "dq3": 1,
797 "cs_n":0})
798
799 if platform is not None and \
800 fpga in ['arty_a7']:
801 # each pin needs a separate direction control
802 spi_0_ios = [
803 Resource("spi_0", 0,
804 Subsignal("dq0", Pins("K17", dir="io")),
805 Subsignal("dq1", Pins("K18", dir="io")),
806 Subsignal("dq2", Pins("L14", dir="io")),
807 Subsignal("dq3", Pins("M14", dir="io")),
808 Subsignal("cs_n", Pins("L13", dir="o")),
809 Subsignal("clk", Pins("L16", dir="o")),
810 Attrs(PULLMODE="NONE", DRIVE="4", IO_TYPE="LVCMOS33"))
811 ]
812 platform.add_resources(spi_0_ios)
813 spi_0_pins = platform.request("spi_0", 0)
814
815 print ("spiflash pins", spi_0_pins)
816
817 # Get Ethernet RMII resource pins
818 ethmac_0_pins = None
819 if False and platform is not None and \
820 fpga in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
821 # Mainly on X3 connector, MDIO on X4 due to lack of pins
822 ethmac_0_ios = [
823 Resource("ethmac_0", 0,
824 Subsignal("mtx_clk", Pins("B19", dir="i")),
825 Subsignal("mtxd", Pins("B12 B9 E6 D6", dir="o")),
826 Subsignal("mtxen", Pins("E7", dir="o")),
827 Subsignal("mtxerr", Pins("D7", dir="o")),
828 Subsignal("mrx_clk", Pins("B11", dir="i")),
829 Subsignal("mrxd", Pins("B6 E9 D9 B8", dir="i")),
830 Subsignal("mrxdv", Pins("C8", dir="i")),
831 Subsignal("mrxerr", Pins("D8", dir="i")),
832 Subsignal("mcoll", Pins("E8", dir="i")),
833 Subsignal("mcrs", Pins("C7", dir="i")),
834 Subsignal("mdc", Pins("B18", dir="o")),
835 Subsignal("md", Pins("A18", dir="io")),
836 Attrs(PULLMODE="NONE", DRIVE="8", SLEWRATE="FAST",
837 IO_TYPE="LVCMOS33"))
838 ]
839 platform.add_resources(ethmac_0_ios)
840 ethmac_0_pins = platform.request("ethmac_0", 0,
841 dir={"mtx_clk":"i", "mtxd":"o",
842 "mtxen":"o",
843 "mtxerr":"o", "mrx_clk":"i",
844 "mrxd":"i",
845 "mrxdv":"i", "mrxerr":"i",
846 "mcoll":"i",
847 "mcrs":"i", "mdc":"o", "md":"io"},
848 xdr={"mtx_clk": 0, "mtxd": 0,
849 "mtxen": 0,
850 "mtxerr": 0, "mrx_clk": 0,
851 "mrxd": 0,
852 "mrxdv": 0, "mrxerr": 0,
853 "mcoll": 0,
854 "mcrs": 0, "mdc": 0, "md": 0})
855 print ("ethmac pins", ethmac_0_pins)
856
857 # Get HyperRAM pins
858 hyperram_pins = None
859 if platform is None:
860 hyperram_pins = HyperRAMPads()
861 elif fpga in ['isim']:
862 hyperram_ios = HyperRAMResource(0, cs_n="B13",
863 dq="E14 C10 B10 E12 D12 A9 D11 D14",
864 rwds="C14", rst_n="E13", ck_p="D13",
865 attrs=Attrs(IO_TYPE="LVCMOS33"))
866 platform.add_resources(hyperram_ios)
867 hyperram_pins = platform.request("hyperram")
868 print ("isim a7 hyperram", hyperram_ios)
869 # Digilent Arty A7-100t
870 elif platform is not None and fpga in ['arty_a7']:
871 hyperram_ios = HyperRAMResource(0, cs_n="V12 V14 U12 U14",
872 dq="D4 D3 F4 F3 G2 H2 D2 E2",
873 rwds="U13", rst_n="T13", ck_p="V10",
874 # ck_n="V11" - for later (DDR)
875 attrs=Attrs(IOSTANDARD="LVCMOS33"))
876 platform.add_resources(hyperram_ios)
877 hyperram_pins = platform.request("hyperram")
878 print ("arty a7 hyperram", hyperram_ios)
879 # VERSA ECP5
880 elif False and platform is not None and fpga in \
881 ['versa_ecp5', 'versa_ecp5_85']:
882 hyperram_ios = HyperRAMResource(0, cs_n="B13",
883 dq="E14 C10 B10 E12 D12 A9 D11 D14",
884 rwds="C14", rst_n="E13", ck_p="D13",
885 attrs=Attrs(IO_TYPE="LVCMOS33"))
886 platform.add_resources(hyperram_ios)
887 hyperram_pins = platform.request("hyperram")
888 print ("versa ecp5 hyperram", hyperram_ios)
889 print ("hyperram pins", hyperram_pins)
890
891 # set up the SOC
892 soc = DDR3SoC(fpga=fpga, dram_cls=dram_cls,
893 # check microwatt_soc.h for these
894 ddrphy_addr=0xfff00000, # DRAM_INIT_BASE, PHY address
895 dramcore_addr=0xc8000000, # DRAM_CTRL_BASE
896 ddr_addr=0x00000000, # DRAM_BASE
897 spi0_addr=0xf0000000, # SPI0_BASE
898 spi0_cfg_addr=0xc0006000, # SPI0_CTRL_BASE
899 eth0_cfg_addr=0xc000c000, # ETH0_CTRL_BASE (4k)
900 eth0_irqno=1, # ETH0_IRQ number (match microwatt)
901 hyperram_addr=0xa0000000, # HYPERRAM_BASE
902 fw_addr=fw_addr,
903 #fw_addr=None,
904 ddr_pins=ddr_pins,
905 uart_pins=uart_pins,
906 uart_irqno=0, # UART_IRQ number (match microwatt)
907 uart_addr=0xc0002000, # UART0_ADDR
908 spi_0_pins=spi_0_pins,
909 ethmac_0_pins=ethmac_0_pins,
910 hyperram_pins=hyperram_pins,
911 firmware=firmware,
912 xics_icp_addr=0xc000_4000, # XICS_ICP_BASE
913 xics_ics_addr=0xc000_5000, # XICS_ICS_BASE
914 clk_freq=clk_freq,
915 add_cpu=True)
916
917 if toolchain == 'Trellis':
918 # add -abc9 option to yosys synth_ecp5
919 #os.environ['NMIGEN_synth_opts'] = '-abc9 -nowidelut'
920 #os.environ['NMIGEN_synth_opts'] = '-abc9'
921 os.environ['NMIGEN_synth_opts'] = '-nowidelut'
922
923 if platform is not None:
924 # build and upload it
925 if fpga == 'isim':
926 platform.build(soc, do_program=False,
927 do_build=True, build_dir="build_simsoc")
928 else:
929 platform.build(soc, do_program=True)
930 else:
931 # for now, generate verilog
932 vl = verilog.convert(soc, ports=soc.ports())
933 with open("ls2.v", "w") as f:
934 f.write(vl)
935
936
937 # urrr this gets exec()d by the build process without arguments
938 # which screws up. use the arty_a7_ls2.py etc. with no arguments
939 if __name__ == '__main__':
940 fpga = None
941 firmware = None
942 if len(sys.argv) >= 2:
943 fpga = sys.argv[1]
944 if len(sys.argv) >= 3:
945 firmware = sys.argv[2]
946 build_platform(fpga, firmware)