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
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 icarusversa
import IcarusVersaPlatform
61 # Clock-Reset Generator (works for all ECP5 platforms)
62 from ecp5_crg
import ECP5CRG
63 from arty_crg
import ArtyA7CRG
68 def sim_ddr3_settings(clk_freq
=100e6
):
69 tck
= 2/(2*2*clk_freq
)
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
)
84 dfi_databits
=4*databits
,
89 rdcmdphase
=rdcmdphase
,
90 wrcmdphase
=wrcmdphase
,
93 read_latency
=2 + cl_sys_latency
+ 2 + log2_int(4//nphases
) + 4,
94 write_latency
=cwl_sys_latency
98 class WB64to32Convert(Elaboratable
):
99 """Microwatt IO wishbone slave 64->32 bits converter
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.
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.
108 def __init__(self
, master
, slave
):
112 def elaborate(self
, platform
):
114 comb
, sync
= m
.d
.comb
, m
.d
.sync
115 master
, slave
= self
.master
, self
.slave
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)
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)
133 # Start cycle downstream
134 sync
+= slave
.cyc
.eq(1)
135 sync
+= slave
.stb
.eq(1)
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
)
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
))
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.
152 with m
.If(master
.we
):
153 sync
+= slave
.dat_w
.eq(master
.dat_w
[:32])
154 sync
+= slave
.sel
.eq(master
.sel
[:4])
156 # Wait for ack on BOTTOM half
157 m
.next
= "WAIT_ACK_BOT"
160 with m
.If(master
.we
):
161 sync
+= slave
.dat_w
.eq(master
.dat_w
[32:])
162 sync
+= slave
.sel
.eq(master
.sel
[4:])
164 # Bump LSB of address
165 sync
+= slave
.adr
[0].eq(1)
167 # Wait for ack on TOP half
168 m
.next
= "WAIT_ACK_TOP"
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)
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
)
183 # Do we have a "top" part as well ?
184 with m
.If(has_top_r
):
186 with m
.If(master
.we
):
187 sync
+= slave
.dat_w
.eq(master
.dat_w
[32:])
188 sync
+= slave
.sel
.eq(master
.sel
[4:])
190 # Bump address and set STB
191 sync
+= slave
.adr
[0].eq(1)
192 sync
+= slave
.stb
.eq(1)
195 m
.next
= "WAIT_ACK_TOP"
198 # We are done, ack up, clear cyc downstram
199 sync
+= slave
.cyc
.eq(0)
200 sync
+= slave
.stb
.eq(0)
202 # And ack & unstall upstream
203 sync
+= master
.ack
.eq(1)
204 if hasattr(master
, "stall"):
205 sync
+= master
.stall
.eq(0)
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)
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
)
222 # We are done, ack up, clear cyc downstram
223 sync
+= slave
.cyc
.eq(0)
224 sync
+= slave
.stb
.eq(0)
226 # And ack & unstall upstream
227 sync
+= master
.ack
.eq(1)
228 if hasattr(master
, "stall"):
229 sync
+= master
.stall
.eq(0)
237 class DDR3SoC(SoC
, Elaboratable
):
238 def __init__(self
, *,
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,
250 xics_icp_addr
=None, xics_ics_addr
=None,
254 # wishbone routing is as follows:
265 # arbiter------------------------------------------+
267 # +---decoder----+--------+---------+-------+--------+ |
269 # uart XICS CSRs DRAM XIP SPI HyperRAM EthMAC
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,
275 features
={"cti", "bte", "stall"})
276 self
._decoder
= wishbone
.Decoder(addr_width
=30, data_width
=32,
278 features
={"cti", "bte", "stall"})
280 # default firmware name
282 firmware
= "firmware/main.bin"
284 # set up clock request generator
286 if fpga
in ['versa_ecp5', 'versa_ecp5_85', 'isim', 'ulx3s',
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
)
294 # set up CPU, with 64-to-32-bit downconverters
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
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
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
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
330 # SRAM (but actually a ROM, for firmware)
331 if fw_addr
is not None:
332 print ("fw at address %x" % fw_addr
)
334 self
.bootmem
= SRAMPeripheral(size
=0x8000, data_width
=sram_width
,
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
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
354 # SRAM (read-writeable BRAM)
355 self
.ram
= SRAMPeripheral(size
=4096)
356 self
._decoder
.add(self
.ram
.bus
, addr
=0x8000000) # at 0x8000_0000
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
,
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,
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
)
377 # SDRAM module using opencores sdr_ctrl
379 class MT48LC16M16(SDRModule):
385 technology_timings = _TechnologyTimings(tREFI=64e6/8192,
389 speedgrade_timings = {"default": _SpeedgradeTimings(tRP=20,
398 if ddr_pins
is not None: # or fpga == 'sim':
399 ddrmodule
= dram_cls(clk_freq
, "1:2") # match DDR3 ASIC P/N
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"})
408 # HOWEVER, when the ASyncBridge is deployed, the two domains
409 # must NOT be renamed, instead this used:
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
418 self
.ddrphy
= FakePHY(module
=ddrmodule
,
419 settings
=sim_ddr3_settings(clk_freq
),
420 verbosity
=SDRAM_VERBOSE_DBG
,
423 self
.ddrphy
= drs(ECP5DDRPHY(ddr_pins
, sys_clk_freq
=clk_freq
))
424 self
._decoder
.add(self
.ddrphy
.bus
, addr
=ddrphy_addr
)
426 dramcore
= gramCore(phy
=self
.ddrphy
,
427 geom_settings
=ddrmodule
.geom_settings
,
428 timing_settings
=ddrmodule
.timing_settings
,
431 self
.dramcore
= dramcore
433 self
.dramcore
= drs(dramcore
)
434 self
._decoder
.add(self
.dramcore
.bus
, addr
=dramcore_addr
)
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'})
442 self
.drambone
= drambone
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
)
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")
454 self
.sram
= SRAMPeripheral(size
=0x8000,
455 data_width
=sram_width
,
457 self
._decoder
.add(self
.sram
.bus
, addr
=0x0) # RAM at 0x0
460 if spi_0_pins
is not None and fpga
in ['sim',
462 'rcs_arctic_tern_bmc_card',
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',
472 'rcs_arctic_tern_bmc_card',
474 spi0_is_lattice_ecp5_clk
= True
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
,
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
)
492 if ethmac_0_pins
is not None and fpga
in ['versa_ecp5',
495 self
.eth_irq
= IRQLine()
496 # The OpenCores Ethernet MAC contains two independent Wishbone
497 # interfaces, a slave (configuration) interface and a master (DMA)
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
)
504 # HyperRAM modules *plural*. Assumes using a Quad PMOD by Piotr
505 # Esden, sold by 1bitsquared, only doing one CS_N enable at the
507 if hyperram_pins
is not None:
508 self
.hyperram
= HyperRAM(io
=hyperram_pins
, phy_kls
=HyperRAMPHY
,
510 latency
=7) # Winbond W956D8MBYA
511 self
._decoder
.add(self
.hyperram
.bus
, addr
=hyperram_addr
)
513 self
.memory_map
= self
._decoder
.bus
.memory_map
515 self
.clk_freq
= clk_freq
518 def elaborate(self
, platform
):
522 # add the peripherals and clock-reset-generator
523 if platform
is not None and hasattr(self
, "crg"):
524 m
.submodules
.sysclk
= self
.crg
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)
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
)
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())
584 # add blinky lights so we know FPGA is alive
585 if platform
is not None:
586 m
.submodules
.blinky
= Blinky()
588 # connect the arbiter (of wishbone masters)
589 # to the decoder (addressing wishbone slaves)
590 comb
+= self
._arbiter
.bus
.connect(self
._decoder
.bus
)
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
598 # wire up the CPU interrupts from the GenericInterrupt
599 comb
+= self
.int_level_i
.eq(self
.intc
.ip
)
602 comb
+= self
.pbus
.stall
.eq(self
.pbus
.cyc
& ~self
.pbus
.ack
)
603 comb
+= self
.sbus
.stall
.eq(self
.sbus
.cyc
& ~self
.sbus
.ack
)
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
)
626 # add uart16550 verilog source. assumes a directory
627 # structure where ls2 has been checked out in a common
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
)
635 self
.uart
.add_verilog_source(fname
, platform
)
637 if hasattr(self
, "spi0"):
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
)
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
)
653 self
.spi0
.add_verilog_source(fname
, platform
)
655 if hasattr(self
, "eth0"):
656 # add ethernet submodule
657 m
.submodules
.eth0
= ethmac
= self
.eth0
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
)
668 self
.eth0
.add_verilog_source(fname
, platform
)
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
)
680 # puzzlingly the only IO ports needed are peripheral pins,
681 # and at the moment that's just UART tx/rx.
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()]
702 def build_platform(fpga
, firmware
):
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
,
712 toolchain
= {'arty_a7': "yosys_nextpnr",
713 'versa_ecp5': 'Trellis',
714 'versa_ecp5_85': 'Trellis',
719 dram_cls
= {'arty_a7': None,
720 'versa_ecp5': MT41K64M16
,
721 'versa_ecp5_85': MT41K64M16
,
722 #'versa_ecp5': MT41K256M16,
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
734 print ("platform", fpga
, firmware
, platform
)
736 # set clock frequency
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...
748 if fpga
== 'arty_a7':
753 # select a firmware address
755 if firmware
is not None:
756 fw_addr
= 0xff00_0000 # firmware at HI address, now
758 print ("fpga", fpga
, "firmware", firmware
)
760 # get UART resource pins
761 if platform
is not None:
762 uart_pins
= platform
.request("uart", 0)
764 uart_pins
= Record([('tx', 1), ('rx', 1)], name
="uart_0")
766 # get DDR resource pins, disable if clock frequency is below 50 mhz for now
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,
774 "odt":4, "ras":4, "cas":4, "we":4,
777 # Get SPI resource pins
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
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"))
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,
799 if platform
is not None and \
801 # each pin needs a separate direction control
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"))
812 platform
.add_resources(spi_0_ios
)
813 spi_0_pins
= platform
.request("spi_0", 0)
815 print ("spiflash pins", spi_0_pins
)
817 # Get Ethernet RMII resource pins
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
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",
839 platform
.add_resources(ethmac_0_ios
)
840 ethmac_0_pins
= platform
.request("ethmac_0", 0,
841 dir={"mtx_clk":"i", "mtxd":"o",
843 "mtxerr":"o", "mrx_clk":"i",
845 "mrxdv":"i", "mrxerr":"i",
847 "mcrs":"i", "mdc":"o", "md":"io"},
848 xdr
={"mtx_clk": 0, "mtxd": 0,
850 "mtxerr": 0, "mrx_clk": 0,
852 "mrxdv": 0, "mrxerr": 0,
854 "mcrs": 0, "mdc": 0, "md": 0})
855 print ("ethmac pins", ethmac_0_pins
)
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
)
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
)
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
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
,
912 xics_icp_addr
=0xc000_4000, # XICS_ICP_BASE
913 xics_ics_addr
=0xc000_5000, # XICS_ICS_BASE
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'
923 if platform
is not None:
924 # build and upload it
926 platform
.build(soc
, do_program
=False,
927 do_build
=True, build_dir
="build_simsoc")
929 platform
.build(soc
, do_program
=True)
931 # for now, generate verilog
932 vl
= verilog
.convert(soc
, ports
=soc
.ports())
933 with
open("ls2.v", "w") as f
:
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__':
942 if len(sys
.argv
) >= 2:
944 if len(sys
.argv
) >= 3:
945 firmware
= sys
.argv
[2]
946 build_platform(fpga
, firmware
)