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>
5 # Based on code from LambaConcept, from the gram example which is BSD-2-License
6 # https://github.com/jeanthom/gram/tree/master/examples
8 # Modifications for the Libre-SOC Project funded by NLnet and NGI POINTER
9 # under EU Grants 871528 and 957073, under the LGPLv3+ License
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 from nmigen_boards
.resources
.interface
import UARTResource
20 from nmigen_stdio
.serial
import AsyncSerial
23 from nmigen_boards
.resources
.memory
import HyperRAMResource
24 from lambdasoc
.periph
.hyperram
import HyperRAM
, HyperRAMPads
, HyperRAMPHY
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
41 from gram
.common
import (PhySettings
, get_cl_cw
, get_sys_latency
,
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
50 from nmigen
.build
import Resource
51 from nmigen
.build
import Subsignal
52 from nmigen
.build
import Pins
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 nmigen_boards
.orangecrab_r0_2
import OrangeCrabR0_2_85k_Platform
61 from icarusversa
import IcarusVersaPlatform
62 # Clock-Reset Generator (works for all ECP5 platforms)
63 from ecp5_crg
import ECP5CRG
64 from arty_crg
import ArtyA7CRG
69 def sim_ddr3_settings(clk_freq
=100e6
):
70 tck
= 2/(2*2*clk_freq
)
76 cl
, cwl
= get_cl_cw("DDR3", tck
)
77 cl_sys_latency
= get_sys_latency(nphases
, cl
)
78 cwl_sys_latency
= get_sys_latency(nphases
, cwl
)
79 rdcmdphase
, rdphase
= get_sys_phases(nphases
, cl_sys_latency
, cl
)
80 wrcmdphase
, wrphase
= get_sys_phases(nphases
, cwl_sys_latency
, cwl
)
85 dfi_databits
=4*databits
,
90 rdcmdphase
=rdcmdphase
,
91 wrcmdphase
=wrcmdphase
,
94 read_latency
=2 + cl_sys_latency
+ 2 + log2_int(4//nphases
) + 4,
95 write_latency
=cwl_sys_latency
99 class WB64to32Convert(Elaboratable
):
100 """Microwatt IO wishbone slave 64->32 bits converter
102 For timing reasons, this adds a one cycle latch on the way both
103 in and out. This relaxes timing and routing pressure on the "main"
104 memory bus by moving all simple IOs to a slower 32-bit bus.
106 This implementation is rather dumb at the moment, no stash buffer,
107 so we stall whenever that latch is busy. This can be improved.
109 def __init__(self
, master
, slave
):
113 def elaborate(self
, platform
):
115 comb
, sync
= m
.d
.comb
, m
.d
.sync
116 master
, slave
= self
.master
, self
.slave
123 with m
.State("IDLE"):
124 # Clear ACK (and has_top_r) in case it was set
125 sync
+= master
.ack
.eq(0)
126 sync
+= has_top_r
.eq(0)
128 # Do we have a cycle ?
129 with m
.If(master
.cyc
& master
.stb
):
130 # Stall master until we are done, we are't (yet) pipelining
131 # this, it's all slow IOs.
132 sync
+= master
.stall
.eq(1)
134 # Start cycle downstream
135 sync
+= slave
.cyc
.eq(1)
136 sync
+= slave
.stb
.eq(1)
138 # Do we have a top word and/or a bottom word ?
139 comb
+= has_top
.eq(master
.sel
[4:].bool())
140 comb
+= has_bot
.eq(master
.sel
[:4].bool())
141 # record the has_top flag for the next FSM state
142 sync
+= has_top_r
.eq(has_top
)
144 # Copy write enable to IO out, copy address as well,
145 # LSB is set later based on HI/LO
146 sync
+= slave
.we
.eq(master
.we
)
147 sync
+= slave
.adr
.eq(Cat(0, master
.adr
))
149 # If we have a bottom word, handle it first, otherwise
150 # send the top word down. XXX Split the actual mux out
151 # and only generate a control signal.
153 with m
.If(master
.we
):
154 sync
+= slave
.dat_w
.eq(master
.dat_w
[:32])
155 sync
+= slave
.sel
.eq(master
.sel
[:4])
157 # Wait for ack on BOTTOM half
158 m
.next
= "WAIT_ACK_BOT"
161 with m
.If(master
.we
):
162 sync
+= slave
.dat_w
.eq(master
.dat_w
[32:])
163 sync
+= slave
.sel
.eq(master
.sel
[4:])
165 # Bump LSB of address
166 sync
+= slave
.adr
[0].eq(1)
168 # Wait for ack on TOP half
169 m
.next
= "WAIT_ACK_TOP"
172 with m
.State("WAIT_ACK_BOT"):
173 # If we aren't stalled by the device, clear stb
174 if hasattr(slave
, "stall"):
175 with m
.If(~slave
.stall
):
176 sync
+= slave
.stb
.eq(0)
179 with m
.If(slave
.ack
):
180 # If it's a read, latch the data
181 with m
.If(~slave
.we
):
182 sync
+= master
.dat_r
[:32].eq(slave
.dat_r
)
184 # Do we have a "top" part as well ?
185 with m
.If(has_top_r
):
187 with m
.If(master
.we
):
188 sync
+= slave
.dat_w
.eq(master
.dat_w
[32:])
189 sync
+= slave
.sel
.eq(master
.sel
[4:])
191 # Bump address and set STB
192 sync
+= slave
.adr
[0].eq(1)
193 sync
+= slave
.stb
.eq(1)
196 m
.next
= "WAIT_ACK_TOP"
199 # We are done, ack up, clear cyc downstram
200 sync
+= slave
.cyc
.eq(0)
201 sync
+= slave
.stb
.eq(0)
203 # And ack & unstall upstream
204 sync
+= master
.ack
.eq(1)
205 if hasattr(master
, "stall"):
206 sync
+= master
.stall
.eq(0)
211 with m
.State("WAIT_ACK_TOP"):
212 # If we aren't stalled by the device, clear stb
213 if hasattr(slave
, "stall"):
214 with m
.If(~slave
.stall
):
215 sync
+= slave
.stb
.eq(0)
218 with m
.If(slave
.ack
):
219 # If it's a read, latch the data
220 with m
.If(~slave
.we
):
221 sync
+= master
.dat_r
[32:].eq(slave
.dat_r
)
223 # We are done, ack up, clear cyc downstram
224 sync
+= slave
.cyc
.eq(0)
225 sync
+= slave
.stb
.eq(0)
227 # And ack & unstall upstream
228 sync
+= master
.ack
.eq(1)
229 if hasattr(master
, "stall"):
230 sync
+= master
.stall
.eq(0)
238 class DDR3SoC(SoC
, Elaboratable
):
239 def __init__(self
, *,
242 uart_pins
=None, spi_0_pins
=None, ethmac_0_pins
=None,
243 ddr_pins
=None, ddrphy_addr
=None,
244 dramcore_addr
=None, ddr_addr
=None,
245 fw_addr
=0x0000_0000, firmware
=None,
246 uart_addr
=None, uart_irqno
=0,
247 spi0_addr
=None, spi0_cfg_addr
=None,
248 eth0_cfg_addr
=None, eth0_irqno
=None,
251 xics_icp_addr
=None, xics_ics_addr
=None,
255 # wishbone routing is as follows:
266 # arbiter------------------------------------------+
268 # +---decoder----+--------+---------+-------+--------+ |
270 # uart XICS CSRs DRAM XIP SPI HyperRAM EthMAC
272 # set up wishbone bus arbiter and decoder. arbiter routes,
273 # decoder maps local-relative addressed satellites to global addresses
274 self
._arbiter
= wishbone
.Arbiter(addr_width
=30, data_width
=32,
276 features
={"cti", "bte", "stall"})
277 self
._decoder
= wishbone
.Decoder(addr_width
=30, data_width
=32,
279 features
={"cti", "bte", "stall"})
281 # default firmware name
283 firmware
= "firmware/main.bin"
285 # set up clock request generator
287 if fpga
in ['versa_ecp5', 'versa_ecp5_85', 'isim', 'ulx3s',
291 self
.crg
= ECP5CRG(clk_freq
, dram_clk_freq
=None, pod_bits
=pod_bits
)
292 if fpga
in ['arty_a7']:
293 self
.crg
= ArtyA7CRG(clk_freq
)
295 # set up CPU, with 64-to-32-bit downconverters
297 self
.cpu
= ExternalCore(name
="ext_core")
298 cvtdbus
= wishbone
.Interface(addr_width
=30, data_width
=32,
299 granularity
=8, features
={'stall'})
300 cvtibus
= wishbone
.Interface(addr_width
=30, data_width
=32,
301 granularity
=8, features
={'stall'})
302 self
.dbusdowncvt
= WB64to32Convert(self
.cpu
.dbus
, cvtdbus
)
303 self
.ibusdowncvt
= WB64to32Convert(self
.cpu
.ibus
, cvtibus
)
304 self
._arbiter
.add(cvtibus
) # I-Cache Master
305 self
._arbiter
.add(cvtdbus
) # D-Cache Master. TODO JTAG master
306 self
.cvtibus
= cvtibus
307 self
.cvtdbus
= cvtdbus
309 # CPU interrupt controller, needs stall to be added, also
310 # compat with wishbone.Interface
311 self
.intc
= GenericInterruptController(width
=len(self
.cpu
.irq
))
312 self
.xics_icp
= icp
= XICS_ICP()
313 self
.xics_ics
= ics
= XICS_ICS()
314 self
.int_level_i
= self
.xics_ics
.int_level_i
316 self
.pbus
= pbus
= wishbone
.Interface(name
="xics_icp_bus",
317 addr_width
=6, data_width
=32,
318 granularity
=8, features
={'stall'})
319 self
.sbus
= sbus
= wishbone
.Interface(name
="xics_ics_bus",
320 addr_width
=10, data_width
=32,
321 granularity
=8, features
={'stall'})
322 pmap
= MemoryMap(addr_width
=8, data_width
=8, name
="icp_map")
323 pbus
.memory_map
= pmap
324 self
._decoder
.add(pbus
, addr
=xics_icp_addr
) # ICP addr
326 smap
= MemoryMap(addr_width
=12, data_width
=8, name
="ics_map")
327 sbus
.memory_map
= smap
328 self
._decoder
.add(sbus
, addr
=xics_ics_addr
) # ICP addr
331 # SRAM (but actually a ROM, for firmware)
332 if fw_addr
is not None:
333 print ("fw at address %x" % fw_addr
)
335 self
.bootmem
= SRAMPeripheral(size
=0x8000, data_width
=sram_width
,
337 if firmware
is not None:
338 with
open(firmware
, "rb") as f
:
339 words
= iter(lambda: f
.read(sram_width
// 8), b
'')
340 bios
= [int.from_bytes(w
, "little") for w
in words
]
341 self
.bootmem
.init
= bios
342 self
._decoder
.add(self
.bootmem
.bus
, addr
=fw_addr
) # ROM at fw_addr
344 # System Configuration info
345 # offset executable ELF payload at 6 megabyte offset (2<<20)
346 spi_offset
= 2<<20 if (spi_0_pins
is not None) else None
347 dram_offset
= ddr_addr
if (ddr_pins
is not None) else None
348 self
.syscon
= MicrowattSYSCON(sys_clk_freq
=clk_freq
,
349 has_uart
=(uart_pins
is not None),
350 spi_offset
=spi_offset
,
351 dram_addr
=dram_offset
)
352 self
._decoder
.add(self
.syscon
.bus
, addr
=0xc0000000) # at 0xc000_0000
355 # SRAM (read-writeable BRAM)
356 self
.ram
= SRAMPeripheral(size
=4096)
357 self
._decoder
.add(self
.ram
.bus
, addr
=0x8000000) # at 0x8000_0000
359 # UART at 0xC000_2000, convert 32-bit bus down to 8-bit in an odd way
360 if uart_pins
is not None:
361 # sigh actual UART in microwatt is 8-bit
362 self
.uart_irq
= IRQLine()
363 self
.uart
= UART16550(data_width
=8, pins
=uart_pins
,
366 # but (see soc.vhdl) 8-bit regs are addressed at 32-bit locations
367 # strictly speaking this is a nmigen-soc "sparse" arrangement
368 # which should be handled by MemoryMap, but needs investigation
369 cvtuartbus
= wishbone
.Interface(addr_width
=5, data_width
=32,
372 umap
= MemoryMap(addr_width
=7, data_width
=8, name
="uart_map")
373 cvtuartbus
.memory_map
= umap
374 self
._decoder
.add(cvtuartbus
, addr
=uart_addr
) # 16550 UART addr
375 self
.cvtuartbus
= cvtuartbus
376 self
.intc
.add_irq(self
.uart
.irq
, index
=uart_irqno
)
378 # SDRAM module using opencores sdr_ctrl
380 class MT48LC16M16(SDRModule):
386 technology_timings = _TechnologyTimings(tREFI=64e6/8192,
390 speedgrade_timings = {"default": _SpeedgradeTimings(tRP=20,
399 if ddr_pins
is not None: # or fpga == 'sim':
400 ddrmodule
= dram_cls(clk_freq
, "1:2") # match DDR3 ASIC P/N
402 # remap both the sync domain (wherever it occurs) and
403 # the sync2x domain. technically this should NOT be done.
404 # it's a bit of a mess. ok: this should be done only
405 # when dramsync===sync (and dramsync2x===sync2x)
406 drs
= DomainRenamer({"sync": "dramsync",
407 "sync2x": "dramsync2x"})
409 # HOWEVER, when the ASyncBridge is deployed, the two domains
410 # must NOT be renamed, instead this used:
412 # and then the ASyncBridge takes care of the two.
413 # but, back in ecp5_crg.py, when ASyncBridge is added,
414 # dram_clk_freq must be passed to ECP5CRG, which will call
415 # ECP5CRG.phase2_domain on your behalf, setting up the
416 # necessary dramsync2x which is needed for the xdr=4 IOpads
419 self
.ddrphy
= FakePHY(module
=ddrmodule
,
420 settings
=sim_ddr3_settings(clk_freq
),
421 verbosity
=SDRAM_VERBOSE_DBG
,
424 self
.ddrphy
= drs(ECP5DDRPHY(ddr_pins
, sys_clk_freq
=clk_freq
))
425 self
._decoder
.add(self
.ddrphy
.bus
, addr
=ddrphy_addr
)
427 dramcore
= gramCore(phy
=self
.ddrphy
,
428 geom_settings
=ddrmodule
.geom_settings
,
429 timing_settings
=ddrmodule
.timing_settings
,
432 self
.dramcore
= dramcore
434 self
.dramcore
= drs(dramcore
)
435 self
._decoder
.add(self
.dramcore
.bus
, addr
=dramcore_addr
)
437 # map the DRAM onto Wishbone, XXX use stall but set classic below
438 # XXX WHEN ADDING ASYNCBRIDGE IT IS THE **BRIDGE** THAT MUST
439 # XXX HAVE THE STALL SIGNAL, AND THE **BRIDGE** THAT MUST HAVE
440 # XXX stall=stb&~ack APPLIED
441 drambone
= gramWishbone(dramcore
, features
={'stall'})
443 self
.drambone
= drambone
445 self
.drambone
= drs(drambone
)
446 # XXX ADD THE ASYNCBRIDGE NOT THE DRAMBONE.BUS, THEN
447 # XXX ADD DRAMBONE.BUS TO ASYNCBRIDGE
448 self
._decoder
.add(self
.drambone
.bus
, addr
=ddr_addr
)
450 # additional SRAM at address if DRAM is not also at 0x0
451 # (TODO, check Flash, and HyperRAM as well)
452 if (ddr_pins
is None or ddr_addr
!= 0x0) and fw_addr
!= 0:
453 print ("SRAM 0x8000 at address 0x0")
455 self
.sram
= SRAMPeripheral(size
=0x8000,
456 data_width
=sram_width
,
458 self
._decoder
.add(self
.sram
.bus
, addr
=0x0) # RAM at 0x0
461 if spi_0_pins
is not None and fpga
in ['sim',
463 'rcs_arctic_tern_bmc_card',
467 # The Lattice ECP5 devices require special handling on the
468 # dedicated SPI clock line, which is shared with the internal
469 # SPI controller used for FPGA bitstream loading.
470 spi0_is_lattice_ecp5_clk
= False
471 if fpga
in ['versa_ecp5',
473 'rcs_arctic_tern_bmc_card',
475 spi0_is_lattice_ecp5_clk
= True
477 # Tercel contains two independent Wishbone regions, a
478 # configuration region and the direct API access region,
479 # Set the SPI 0 access region to 16MB, as the FPGA
480 # bitstream Flash device is unlikely to be larger than this.
481 # The main SPI Flash (SPI 1) should be set to at
482 # least 28 bits (256MB) to allow the use of large 4BA devices.
483 self
.spi0
= Tercel(data_width
=32, spi_region_addr_width
=24,
484 adr_offset
=spi0_addr
,
488 lattice_ecp5_usrmclk
=spi0_is_lattice_ecp5_clk
)
489 self
._decoder
.add(self
.spi0
.bus
, addr
=spi0_addr
)
490 self
._decoder
.add(self
.spi0
.cfg_bus
, addr
=spi0_cfg_addr
)
493 if ethmac_0_pins
is not None and fpga
in ['versa_ecp5',
496 self
.eth_irq
= IRQLine()
497 # The OpenCores Ethernet MAC contains two independent Wishbone
498 # interfaces, a slave (configuration) interface and a master (DMA)
500 self
.eth0
= EthMAC(pins
=ethmac_0_pins
, irq
=self
.eth_irq
)
501 self
._arbiter
.add(self
.eth0
.master_bus
)
502 self
._decoder
.add(self
.eth0
.slave_bus
, addr
=eth0_cfg_addr
)
503 self
.intc
.add_irq(self
.eth0
.irq
, index
=eth0_irqno
)
505 # HyperRAM modules *plural*. Assumes using a Quad PMOD by Piotr
506 # Esden, sold by 1bitsquared, only doing one CS_N enable at the
508 if hyperram_pins
is not None:
509 self
.hyperram
= HyperRAM(io
=hyperram_pins
, phy_kls
=HyperRAMPHY
,
511 latency
=7) # Winbond W956D8MBYA
512 self
._decoder
.add(self
.hyperram
.bus
, addr
=hyperram_addr
)
514 self
.memory_map
= self
._decoder
.bus
.memory_map
516 self
.clk_freq
= clk_freq
519 def elaborate(self
, platform
):
523 # add the peripherals and clock-reset-generator
524 if platform
is not None and hasattr(self
, "crg"):
525 m
.submodules
.sysclk
= self
.crg
527 if hasattr(self
, "sram"):
528 m
.submodules
.sram
= self
.sram
529 if hasattr(self
, "bootmem"):
530 m
.submodules
.bootmem
= self
.bootmem
531 m
.submodules
.syscon
= self
.syscon
532 if hasattr(self
, "ram"):
533 m
.submodules
.ram
= self
.ram
534 if hasattr(self
, "uart"):
535 m
.submodules
.uart
= self
.uart
536 comb
+= self
.uart
.cts_i
.eq(1)
537 comb
+= self
.uart
.dsr_i
.eq(1)
538 comb
+= self
.uart
.ri_i
.eq(0)
539 comb
+= self
.uart
.dcd_i
.eq(1)
540 # sigh connect up the wishbone bus manually to deal with
541 # the mis-match on the data. nmigen-soc "sparse" MemoryMap
542 # should be able to deal with this. TODO, investigate
543 uartbus
= self
.uart
.bus
544 comb
+= uartbus
.adr
.eq(self
.cvtuartbus
.adr
)
545 comb
+= uartbus
.stb
.eq(self
.cvtuartbus
.stb
)
546 comb
+= uartbus
.cyc
.eq(self
.cvtuartbus
.cyc
)
547 comb
+= uartbus
.sel
.eq(self
.cvtuartbus
.sel
)
548 comb
+= uartbus
.we
.eq(self
.cvtuartbus
.we
)
549 comb
+= uartbus
.dat_w
.eq(self
.cvtuartbus
.dat_w
) # drops 8..31
550 comb
+= self
.cvtuartbus
.dat_r
.eq(uartbus
.dat_r
) # drops 8..31
551 comb
+= self
.cvtuartbus
.ack
.eq(uartbus
.ack
)
552 # aaand with the WB4-pipeline-to-WB3-classic mismatch, sigh
553 comb
+= uartbus
.stall
.eq(uartbus
.cyc
& ~uartbus
.ack
)
554 comb
+= self
.cvtuartbus
.stall
.eq(uartbus
.stall
)
555 if hasattr(self
, "cpu"):
556 m
.submodules
.intc
= self
.intc
557 m
.submodules
.extcore
= self
.cpu
558 m
.submodules
.dbuscvt
= self
.dbusdowncvt
559 m
.submodules
.ibuscvt
= self
.ibusdowncvt
560 # create stall sigs, assume wishbone classic
561 #ibus, dbus = self.cvtibus, self.cvtdbus
562 #comb += ibus.stall.eq(ibus.stb & ~ibus.ack)
563 #comb += dbus.stall.eq(dbus.stb & ~dbus.ack)
565 m
.submodules
.arbiter
= self
._arbiter
566 m
.submodules
.decoder
= self
._decoder
567 if hasattr(self
, "ddrphy"):
568 m
.submodules
.ddrphy
= self
.ddrphy
569 m
.submodules
.dramcore
= self
.dramcore
570 m
.submodules
.drambone
= drambone
= self
.drambone
571 # grrr, same problem with drambone: not WB4-pipe compliant
572 # XXX TAKE THIS OUT, REPLACE WITH ASYNCBRIDGE HAVING
573 # XXX asyncbridge.bus.stall.eq(asyncbridge.bus.cyc & ...)
574 comb
+= drambone
.bus
.stall
.eq(drambone
.bus
.cyc
& ~drambone
.bus
.ack
)
576 # add hyperram module
577 if hasattr(self
, "hyperram"):
578 m
.submodules
.hyperram
= hyperram
= self
.hyperram
579 # grrr, same problem with hyperram: not WB4-pipe compliant
580 comb
+= hyperram
.bus
.stall
.eq(hyperram
.bus
.cyc
& ~hyperram
.bus
.ack
)
581 # set 3 top CSn lines to zero for now
582 if self
.fpga
== 'arty_a7':
583 comb
+= hyperram
.phy
.rst_n
.eq(ResetSignal())
585 # add blinky lights so we know FPGA is alive
586 if platform
is not None:
587 m
.submodules
.blinky
= Blinky()
589 # connect the arbiter (of wishbone masters)
590 # to the decoder (addressing wishbone slaves)
591 comb
+= self
._arbiter
.bus
.connect(self
._decoder
.bus
)
593 if hasattr(self
, "cpu"):
594 m
.submodules
.xics_icp
= icp
= self
.xics_icp
595 m
.submodules
.xics_ics
= ics
= self
.xics_ics
596 comb
+= icp
.ics_i
.eq(ics
.icp_o
) # connect ICS to ICP
597 comb
+= self
.cpu
.irq
.eq(icp
.core_irq_o
) # connect ICP to core
599 # wire up the CPU interrupts from the GenericInterrupt
600 comb
+= self
.int_level_i
.eq(self
.intc
.ip
)
603 comb
+= self
.pbus
.stall
.eq(self
.pbus
.cyc
& ~self
.pbus
.ack
)
604 comb
+= self
.sbus
.stall
.eq(self
.sbus
.cyc
& ~self
.sbus
.ack
)
606 # and also wire up make_wb_layout() to wishbone.Interface.
607 # really, XICS_ICS and XICS_ICP both need to be converted
608 # to use wishbone.Interface and this all goes
609 comb
+= icp
.bus
.adr
.eq(self
.pbus
.adr
)
610 comb
+= icp
.bus
.dat_w
.eq(self
.pbus
.dat_w
)
611 comb
+= icp
.bus
.cyc
.eq(self
.pbus
.cyc
)
612 comb
+= icp
.bus
.stb
.eq(self
.pbus
.stb
)
613 comb
+= icp
.bus
.we
.eq(self
.pbus
.we
)
614 comb
+= self
.pbus
.ack
.eq(icp
.bus
.ack
)
615 comb
+= self
.pbus
.dat_r
.eq(icp
.bus
.dat_r
)
616 comb
+= ics
.bus
.adr
.eq(self
.sbus
.adr
)
617 comb
+= ics
.bus
.dat_w
.eq(self
.sbus
.dat_w
)
618 comb
+= ics
.bus
.cyc
.eq(self
.sbus
.cyc
)
619 comb
+= ics
.bus
.stb
.eq(self
.sbus
.stb
)
620 comb
+= ics
.bus
.we
.eq(self
.sbus
.we
)
621 comb
+= self
.sbus
.ack
.eq(ics
.bus
.ack
)
622 comb
+= self
.sbus
.dat_r
.eq(ics
.bus
.dat_r
)
627 # add uart16550 verilog source. assumes a directory
628 # structure where ls2 has been checked out in a common
630 # git clone https://github.com/freecores/uart16550
631 opencores_16550
= "../../uart16550/rtl/verilog"
632 pth
= os
.path
.split(__file__
)[0]
633 pth
= os
.path
.join(pth
, opencores_16550
)
634 fname
= os
.path
.abspath(pth
)
636 self
.uart
.add_verilog_source(fname
, platform
)
638 if hasattr(self
, "spi0"):
640 m
.submodules
.spi0
= spi
= self
.spi0
641 # gonna drive me nuts, this.
642 comb
+= spi
.bus
.stall
.eq(spi
.bus
.cyc
& ~spi
.bus
.ack
)
643 comb
+= spi
.cfg_bus
.stall
.eq(spi
.cfg_bus
.cyc
& ~spi
.cfg_bus
.ack
)
645 # add Tercel verilog source. assumes a directory structure where
646 # microwatt has been checked out in a common subdirectory with:
647 # git clone https://git.libre-soc.org/git/microwatt.git tercel-qspi
648 # git checkout 882ace781e4
649 raptor_tercel
= "../../tercel-qspi/tercel"
650 pth
= os
.path
.split(__file__
)[0]
651 pth
= os
.path
.join(pth
, raptor_tercel
)
652 fname
= os
.path
.abspath(pth
)
654 self
.spi0
.add_verilog_source(fname
, platform
)
656 if hasattr(self
, "eth0"):
657 # add ethernet submodule
658 m
.submodules
.eth0
= ethmac
= self
.eth0
660 # add EthMAC verilog source. assumes a directory
661 # structure where the opencores ethmac has been checked out
662 # in a common subdirectory as:
663 # git clone https://github.com/freecores/ethmac
664 opencores_ethmac
= "../../ethmac/rtl/verilog"
665 pth
= os
.path
.split(__file__
)[0]
666 pth
= os
.path
.join(pth
, opencores_ethmac
)
667 fname
= os
.path
.abspath(pth
)
669 self
.eth0
.add_verilog_source(fname
, platform
)
672 pth
= os
.path
.split(__file__
)[0]
673 pth
= os
.path
.join(pth
, '../external_core_top.v')
674 fname
= os
.path
.abspath(pth
)
675 with
open(fname
) as f
:
676 platform
.add_file(fname
, f
)
681 # puzzlingly the only IO ports needed are peripheral pins,
682 # and at the moment that's just UART tx/rx.
684 ports
+= [self
.uart
.tx_o
, self
.uart
.rx_i
]
685 if hasattr(self
, "hyperram"):
686 ports
+= list(self
.hyperram
.ports())
687 if hasattr(self
, "ddrphy"):
688 if hasattr(self
.ddrphy
, "pads"): # real PHY
689 ports
+= list(self
.ddrphy
.pads
.fields
.values())
690 else: # FakePHY, get at the dfii pads, stops deletion of nets
691 for phase
in self
.dramcore
.dfii
.master
.phases
:
692 print ("dfi master", phase
)
693 ports
+= list(phase
.fields
.values())
694 for phase
in self
.dramcore
.dfii
.slave
.phases
:
695 print ("dfi master", phase
)
696 ports
+= list(phase
.fields
.values())
697 for phase
in self
.dramcore
.dfii
._inti
.phases
:
698 print ("dfi master", phase
)
699 ports
+= list(phase
.fields
.values())
700 ports
+= [ClockSignal(), ResetSignal()]
703 def build_platform(fpga
, firmware
):
705 # create a platform selected from the toolchain.
706 platform_kls
= {'versa_ecp5': VersaECP5Platform
,
707 'versa_ecp5_85': VersaECP5Platform85
,
708 'ulx3s': ULX3S_85F_Platform
,
709 'orangecrab': OrangeCrabR0_2_85k_Platform
,
710 'arty_a7': ArtyA7_100Platform
,
711 'isim': IcarusVersaPlatform
,
714 toolchain
= {'arty_a7': "yosys_nextpnr",
715 'versa_ecp5': 'Trellis',
716 'versa_ecp5_85': 'Trellis',
717 'orangecrab': 'Trellis',
722 dram_cls
= {'arty_a7': None,
723 'versa_ecp5': MT41K64M16
,
724 'versa_ecp5_85': MT41K64M16
,
725 #'versa_ecp5': MT41K256M16,
730 if platform_kls
is not None:
731 platform
= platform_kls(toolchain
=toolchain
)
732 if fpga
== 'versa_ecp5_85':
733 platform
.speed
= "7" # HACK. speed grade 7, sigh
737 print ("platform", fpga
, firmware
, platform
)
739 # set clock frequency
744 clk_freq
= 55e6
# below 50 mhz, stops DRAM being enabled
745 if fpga
== 'versa_ecp5':
746 clk_freq
= 50e6
# crank right down to test hyperram
747 if fpga
== 'versa_ecp5_85':
748 # 50MHz works. 100MHz works. 55MHz does NOT work.
749 # Stick with multiples of 50MHz...
751 if fpga
== 'arty_a7':
756 # select a firmware address
758 if firmware
is not None:
759 fw_addr
= 0xff00_0000 # firmware at HI address, now
761 print ("fpga", fpga
, "firmware", firmware
)
763 # get UART resource pins
764 if platform
is not None:
765 if fpga
=="orangecrab":
766 orangecrab_uart
= UARTResource(0, rx
="N17", tx
="M18",
767 attrs
=Attrs(IOSTANDARD
="LVCMOS33"))
768 platform
.add_resources([orangecrab_uart
])
770 uart_pins
= platform
.request("uart", 0)
772 uart_pins
= Record([('tx', 1), ('rx', 1)], name
="uart_0")
774 # get DDR resource pins, disable if clock frequency is below 50 mhz for now
776 if (clk_freq
>= 50e6
and platform
is not None and
777 fpga
in ['versa_ecp5', 'versa_ecp5_85', 'arty_a7', 'isim']):
778 ddr_pins
= platform
.request("ddr3", 0,
779 dir={"dq":"-", "dqs":"-"},
780 xdr
={"rst": 1, "clk":4, "a":4,
782 "odt":4, "ras":4, "cas":4, "we":4,
785 # Get SPI resource pins
787 if platform
is not None and \
788 fpga
in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
789 # Override here to get FlashResource out of the way and enable Tercel
790 # direct access to the SPI flash.
791 # each pin needs a separate direction control
794 Subsignal("dq0", Pins("W2", dir="io")),
795 Subsignal("dq1", Pins("V2", dir="io")),
796 Subsignal("dq2", Pins("Y2", dir="io")),
797 Subsignal("dq3", Pins("W1", dir="io")),
798 Subsignal("cs_n", Pins("R2", dir="o")),
799 Attrs(PULLMODE
="NONE", DRIVE
="4", IO_TYPE
="LVCMOS33"))
801 platform
.add_resources(spi_0_ios
)
802 spi_0_pins
= platform
.request("spi_0", 0, dir={"cs_n":"o"},
803 xdr
={"dq0":1, "dq1": 1,
807 if platform
is not None and \
809 # each pin needs a separate direction control
812 Subsignal("dq0", Pins("K17", dir="io")),
813 Subsignal("dq1", Pins("K18", dir="io")),
814 Subsignal("dq2", Pins("L14", dir="io")),
815 Subsignal("dq3", Pins("M14", dir="io")),
816 Subsignal("cs_n", Pins("L13", dir="o")),
817 Subsignal("clk", Pins("L16", dir="o")),
818 Attrs(PULLMODE
="NONE", DRIVE
="4", IO_TYPE
="LVCMOS33"))
820 platform
.add_resources(spi_0_ios
)
821 spi_0_pins
= platform
.request("spi_0", 0)
823 print ("spiflash pins", spi_0_pins
)
825 # Get Ethernet RMII resource pins
827 if False and platform
is not None and \
828 fpga
in ['versa_ecp5', 'versa_ecp5_85', 'isim']:
829 # Mainly on X3 connector, MDIO on X4 due to lack of pins
831 Resource("ethmac_0", 0,
832 Subsignal("mtx_clk", Pins("B19", dir="i")),
833 Subsignal("mtxd", Pins("B12 B9 E6 D6", dir="o")),
834 Subsignal("mtxen", Pins("E7", dir="o")),
835 Subsignal("mtxerr", Pins("D7", dir="o")),
836 Subsignal("mrx_clk", Pins("B11", dir="i")),
837 Subsignal("mrxd", Pins("B6 E9 D9 B8", dir="i")),
838 Subsignal("mrxdv", Pins("C8", dir="i")),
839 Subsignal("mrxerr", Pins("D8", dir="i")),
840 Subsignal("mcoll", Pins("E8", dir="i")),
841 Subsignal("mcrs", Pins("C7", dir="i")),
842 Subsignal("mdc", Pins("B18", dir="o")),
843 Subsignal("md", Pins("A18", dir="io")),
844 Attrs(PULLMODE
="NONE", DRIVE
="8", SLEWRATE
="FAST",
847 platform
.add_resources(ethmac_0_ios
)
848 ethmac_0_pins
= platform
.request("ethmac_0", 0,
849 dir={"mtx_clk":"i", "mtxd":"o",
851 "mtxerr":"o", "mrx_clk":"i",
853 "mrxdv":"i", "mrxerr":"i",
855 "mcrs":"i", "mdc":"o", "md":"io"},
856 xdr
={"mtx_clk": 0, "mtxd": 0,
858 "mtxerr": 0, "mrx_clk": 0,
860 "mrxdv": 0, "mrxerr": 0,
862 "mcrs": 0, "mdc": 0, "md": 0})
863 print ("ethmac pins", ethmac_0_pins
)
868 hyperram_pins
= HyperRAMPads()
869 elif fpga
in ['isim']:
870 hyperram_ios
= HyperRAMResource(0, cs_n
="B13",
871 dq
="E14 C10 B10 E12 D12 A9 D11 D14",
872 rwds
="C14", rst_n
="E13", ck_p
="D13",
873 attrs
=Attrs(IO_TYPE
="LVCMOS33"))
874 platform
.add_resources(hyperram_ios
)
875 hyperram_pins
= platform
.request("hyperram")
876 print ("isim a7 hyperram", hyperram_ios
)
877 # Digilent Arty A7-100t
878 elif platform
is not None and fpga
in ['arty_a7']:
879 hyperram_ios
= HyperRAMResource(0, cs_n
="V12 V14 U12 U14",
880 dq
="D4 D3 F4 F3 G2 H2 D2 E2",
881 rwds
="U13", rst_n
="T13", ck_p
="V10",
882 # ck_n="V11" - for later (DDR)
883 attrs
=Attrs(IOSTANDARD
="LVCMOS33"))
884 platform
.add_resources(hyperram_ios
)
885 hyperram_pins
= platform
.request("hyperram")
886 print ("arty a7 hyperram", hyperram_ios
)
888 elif False and platform
is not None and fpga
in \
889 ['versa_ecp5', 'versa_ecp5_85']:
890 hyperram_ios
= HyperRAMResource(0, cs_n
="B13",
891 dq
="E14 C10 B10 E12 D12 A9 D11 D14",
892 rwds
="C14", rst_n
="E13", ck_p
="D13",
893 attrs
=Attrs(IO_TYPE
="LVCMOS33"))
894 platform
.add_resources(hyperram_ios
)
895 hyperram_pins
= platform
.request("hyperram")
896 print ("versa ecp5 hyperram", hyperram_ios
)
897 print ("hyperram pins", hyperram_pins
)
900 soc
= DDR3SoC(fpga
=fpga
, dram_cls
=dram_cls
,
901 # check microwatt_soc.h for these
902 ddrphy_addr
=0xfff00000, # DRAM_INIT_BASE, PHY address
903 dramcore_addr
=0xc8000000, # DRAM_CTRL_BASE
904 ddr_addr
=0x00000000, # DRAM_BASE
905 spi0_addr
=0xf0000000, # SPI0_BASE
906 spi0_cfg_addr
=0xc0006000, # SPI0_CTRL_BASE
907 eth0_cfg_addr
=0xc000c000, # ETH0_CTRL_BASE (4k)
908 eth0_irqno
=1, # ETH0_IRQ number (match microwatt)
909 hyperram_addr
=0xa0000000, # HYPERRAM_BASE
914 uart_irqno
=0, # UART_IRQ number (match microwatt)
915 uart_addr
=0xc0002000, # UART0_ADDR
916 spi_0_pins
=spi_0_pins
,
917 ethmac_0_pins
=ethmac_0_pins
,
918 hyperram_pins
=hyperram_pins
,
920 xics_icp_addr
=0xc000_4000, # XICS_ICP_BASE
921 xics_ics_addr
=0xc000_5000, # XICS_ICS_BASE
925 if toolchain
== 'Trellis':
926 # add -abc9 option to yosys synth_ecp5
927 #os.environ['NMIGEN_synth_opts'] = '-abc9 -nowidelut'
928 #os.environ['NMIGEN_synth_opts'] = '-abc9'
929 os
.environ
['NMIGEN_synth_opts'] = '-nowidelut'
931 if platform
is not None:
932 # build and upload it
934 platform
.build(soc
, do_program
=False,
935 do_build
=True, build_dir
="build_simsoc")
937 platform
.build(soc
, do_program
=True)
939 # for now, generate verilog
940 vl
= verilog
.convert(soc
, ports
=soc
.ports())
941 with
open("ls2.v", "w") as f
:
945 # urrr this gets exec()d by the build process without arguments
946 # which screws up. use the arty_a7_ls2.py etc. with no arguments
947 if __name__
== '__main__':
950 if len(sys
.argv
) >= 2:
952 if len(sys
.argv
) >= 3:
953 firmware
= sys
.argv
[2]
954 build_platform(fpga
, firmware
)