implement UART, Timer, SPI Flash & Eth RGMII cores
* These implementations use Harry's proposal for nmigen-stdio & nmigen-soc
This commit is contained in:
parent
2cb3484e33
commit
353b34a135
|
@ -3,6 +3,8 @@ rec {
|
|||
vivado = import ./eda/vivado.nix { inherit pkgs; };
|
||||
nmigen = pkgs.callPackage ./eda/nmigen.nix { };
|
||||
nmigen-boards = pkgs.callPackage ./eda/nmigen-boards.nix { inherit nmigen; };
|
||||
nmigen-stdio = pkgs.callPackage ./eda/nmigen-stdio.nix { inherit nmigen; };
|
||||
nmigen-soc = pkgs.callPackage ./eda/nmigen-soc.nix { inherit nmigen; };
|
||||
scala-spinalhdl = pkgs.callPackage ./eda/scala-spinalhdl.nix {};
|
||||
|
||||
jtagtap = pkgs.callPackage ./cores/jtagtap.nix { inherit nmigen; };
|
||||
|
@ -15,7 +17,7 @@ rec {
|
|||
litex = pkgs.callPackage ./cores/litex.nix { inherit nmigen; };
|
||||
litedram = pkgs.callPackage ./cores/litedram.nix { inherit litex; };
|
||||
|
||||
heavycomps = pkgs.callPackage ./heavycomps.nix { inherit nmigen; };
|
||||
heavycomps = pkgs.callPackage ./heavycomps.nix { inherit nmigen; inherit nmigen-stdio; inherit nmigen-soc; };
|
||||
|
||||
binutils-riscv32 = pkgs.callPackage ./compilers/binutils.nix { platform = "riscv32"; };
|
||||
gcc-riscv32 = pkgs.callPackage ./compilers/gcc.nix { platform = "riscv32"; platform-binutils = binutils-riscv32; };
|
||||
|
@ -23,5 +25,5 @@ rec {
|
|||
gcc-riscv64 = pkgs.callPackage ./compilers/gcc.nix { platform = "riscv64"; platform-binutils = binutils-riscv64; };
|
||||
rust-riscv32i-crates = pkgs.callPackage ./compilers/rust-riscv32i-crates.nix { };
|
||||
|
||||
fw-helloworld = pkgs.callPackage ./firmware { inherit rust-riscv32i-crates binutils-riscv32; };
|
||||
fw-helloworld = pkgs.callPackage ./firmware/testing { inherit rust-riscv32i-crates binutils-riscv32; };
|
||||
}
|
||||
|
|
|
@ -5,9 +5,16 @@ python3Packages.buildPythonPackage {
|
|||
version = "2019-10-13";
|
||||
|
||||
src = fetchgit {
|
||||
url = "https://github.com/m-labs/nmigen-boards";
|
||||
rev = "bc074915d6c67a33447d4730b720b37e3ea4bb3f";
|
||||
sha256 = "1qp32783p0bg6fxfj0wd7fd3if8az7w1r521wcbznxakdrqhrqjd";
|
||||
# Using official master branch
|
||||
#url = "https://github.com/m-labs/nmigen-boards";
|
||||
#rev = "bc074915d6c67a33447d4730b720b37e3ea4bb3f";
|
||||
#sha256 = "1qp32783p0bg6fxfj0wd7fd3if8az7w1r521wcbznxakdrqhrqjd";
|
||||
|
||||
# Using harry's fork
|
||||
url = "https://github.com/HarryHo90sHK/nmigen-boards";
|
||||
rev = "d51e1d5f53aca59d138860d332edf84fdce7cfa3";
|
||||
sha256 = "0yfs81bhcx4pm4q83pjhiibc1zknsllimqb2c753r3vvbcg4frmw";
|
||||
|
||||
leaveDotGit = true;
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
{ stdenv, fetchgit, python3Packages, nmigen, git }:
|
||||
|
||||
python3Packages.buildPythonPackage {
|
||||
name = "nmigen-soc";
|
||||
version = "harry-fork";
|
||||
|
||||
src = fetchgit {
|
||||
# Using harry's fork
|
||||
url = "https://github.com/HarryHo90sHK/nmigen-soc";
|
||||
rev = "89829244034d65d2bf486052af6646e9abec92fe";
|
||||
sha256 = "1dlvyr42shzh61rwj3xgkjr97lvra8vkh7qaxviml7wlw45sfc3h";
|
||||
|
||||
leaveDotGit = true;
|
||||
};
|
||||
|
||||
nativeBuildInputs = [ python3Packages.setuptools_scm git ];
|
||||
propagatedBuildInputs = [ nmigen ];
|
||||
|
||||
meta = with stdenv.lib; {
|
||||
description = "System on Chip toolkit for nMigen";
|
||||
homepage = "https://m-labs.hk";
|
||||
license = licenses.bsd2;
|
||||
maintainers = [ maintainers.sb0 ];
|
||||
};
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
{ stdenv, fetchgit, python3Packages, nmigen, git }:
|
||||
|
||||
python3Packages.buildPythonPackage {
|
||||
name = "nmigen-stdio";
|
||||
version = "harry-fork";
|
||||
|
||||
src = fetchgit {
|
||||
# Using harry's fork
|
||||
url = "https://github.com/HarryHo90sHK/nmigen-stdio";
|
||||
rev = "531465f15e80b26a83989f843bda60e6c93babae";
|
||||
sha256 = "0ln32r7ldd0pndffw076xj6izl86mn5l9l9xmn5wcwrmiby294m1";
|
||||
|
||||
leaveDotGit = true;
|
||||
};
|
||||
|
||||
nativeBuildInputs = [ python3Packages.setuptools_scm git ];
|
||||
propagatedBuildInputs = [ nmigen ];
|
||||
|
||||
meta = with stdenv.lib; {
|
||||
description = "Industry standard I/O for nMigen";
|
||||
homepage = "https://m-labs.hk";
|
||||
license = licenses.bsd2;
|
||||
maintainers = [ maintainers.sb0 ];
|
||||
};
|
||||
}
|
|
@ -1,58 +0,0 @@
|
|||
import argparse
|
||||
|
||||
from nmigen import *
|
||||
from nmigen_boards.versa_ecp5 import VersaECP5Platform
|
||||
|
||||
from heavycomps import uart
|
||||
|
||||
|
||||
class Top(Elaboratable):
|
||||
def __init__(self, baudrate=115200):
|
||||
self.baudrate = baudrate
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
cd_sync = ClockDomain(reset_less=True)
|
||||
m.domains += cd_sync
|
||||
m.d.comb += cd_sync.clk.eq(platform.request("clk100").i)
|
||||
|
||||
string = "Hello World!\r\n"
|
||||
mem = Memory(width=8, depth=len(string),
|
||||
init=[ord(c) for c in string])
|
||||
m.submodules.rdport = rdport = mem.read_port(domain="comb")
|
||||
|
||||
wait = Signal()
|
||||
|
||||
tx = uart.RS232TX(round(2**32*self.baudrate/100e6))
|
||||
m.submodules.tx = tx
|
||||
m.d.comb += [
|
||||
tx.stb.eq(~wait),
|
||||
tx.data.eq(rdport.data),
|
||||
platform.request("uart").tx.o.eq(tx.tx)
|
||||
]
|
||||
|
||||
release = Signal()
|
||||
counter = Signal(25)
|
||||
m.d.sync += Cat(counter, release).eq(counter + 1)
|
||||
with m.If(release):
|
||||
m.d.sync += wait.eq(0)
|
||||
|
||||
with m.If(~wait & tx.ack):
|
||||
with m.If(rdport.addr == len(string) - 1):
|
||||
m.d.sync += rdport.addr.eq(0)
|
||||
m.d.sync += wait.eq(1)
|
||||
with m.Else():
|
||||
m.d.sync += rdport.addr.eq(rdport.addr + 1)
|
||||
|
||||
return m
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("build_dir")
|
||||
args = parser.parse_args()
|
||||
VersaECP5Platform().build(Top(), build_dir=args.build_dir)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,9 +0,0 @@
|
|||
{ pkgs, hx }:
|
||||
|
||||
pkgs.runCommand "helloworld-bitstream" {
|
||||
buildInputs = [ (pkgs.python3.withPackages(ps: [hx.nmigen hx.nmigen-boards hx.heavycomps])) pkgs.yosys ];
|
||||
}
|
||||
''
|
||||
export VIVADO=${hx.vivado}/bin/vivado
|
||||
python ${./helloworld_kintex7.py} $out
|
||||
''
|
|
@ -1,53 +0,0 @@
|
|||
import argparse
|
||||
|
||||
from nmigen import *
|
||||
from nmigen_boards.kc705 import KC705Platform
|
||||
|
||||
from heavycomps import uart
|
||||
|
||||
|
||||
class Top(Elaboratable):
|
||||
def __init__(self, baudrate=115200):
|
||||
self.baudrate = baudrate
|
||||
self.clk156_p = Signal()
|
||||
self.clk156_n = Signal()
|
||||
self.serial_tx = Signal()
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
cd_sync = ClockDomain(reset_less=True)
|
||||
m.domains += cd_sync
|
||||
m.submodules.clock = Instance("BUFG",
|
||||
i_I=platform.request("clk156").i, o_O=cd_sync.clk)
|
||||
|
||||
string = "Hello World!\r\n"
|
||||
mem = Memory(width=8, depth=len(string),
|
||||
init=[ord(c) for c in string])
|
||||
m.submodules.rdport = rdport = mem.read_port(domain="comb")
|
||||
|
||||
tx = uart.RS232TX(round(2**32*self.baudrate/156e6))
|
||||
m.submodules.tx = tx
|
||||
m.d.comb += [
|
||||
tx.stb.eq(1),
|
||||
tx.data.eq(rdport.data),
|
||||
platform.request("uart").tx.o.eq(tx.tx)
|
||||
]
|
||||
|
||||
with m.If(tx.ack):
|
||||
with m.If(rdport.addr == len(string) - 1):
|
||||
m.d.sync += rdport.addr.eq(0)
|
||||
with m.Else():
|
||||
m.d.sync += rdport.addr.eq(rdport.addr + 1)
|
||||
|
||||
return m
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("build_dir")
|
||||
args = parser.parse_args()
|
||||
KC705Platform().build(Top(), build_dir=args.build_dir)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,11 +0,0 @@
|
|||
{ pkgs, hx }:
|
||||
|
||||
pkgs.runCommand "simplesoc-bitstream" {
|
||||
buildInputs = [ (pkgs.python3.withPackages(ps: [hx.nmigen hx.nmigen-boards hx.heavycomps hx.minerva])) pkgs.yosys ];
|
||||
}
|
||||
''
|
||||
export YOSYS=${pkgs.yosys}/bin/yosys
|
||||
export NEXTPNR_ECP5=${pkgs.nextpnr}/bin/nextpnr-ecp5
|
||||
export ECPPACK=${pkgs.trellis}/bin/ecppack
|
||||
python ${./simplesoc_ecp5.py} ${hx.fw-helloworld}/helloworld.bin $out
|
||||
''
|
|
@ -1,99 +0,0 @@
|
|||
import os
|
||||
import argparse
|
||||
import struct
|
||||
|
||||
from nmigen import *
|
||||
from nmigen.back import pysim
|
||||
from nmigen_boards.versa_ecp5 import VersaECP5Platform
|
||||
|
||||
from heavycomps import uart, wishbone
|
||||
from minerva.core import Minerva
|
||||
|
||||
|
||||
class SimpleWishboneSerial(Elaboratable):
|
||||
def __init__(self, tx, sys_clk_freq, baudrate=115200):
|
||||
self.tx = tx
|
||||
self.bus = wishbone.Interface()
|
||||
self.ftw = round(2**32*baudrate/sys_clk_freq)
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
m.submodules.tx = tx = uart.RS232TX(self.ftw)
|
||||
m.d.comb += [
|
||||
tx.stb.eq(self.bus.cyc & self.bus.stb & self.bus.we),
|
||||
tx.data.eq(self.bus.dat_w),
|
||||
self.bus.ack.eq(tx.ack),
|
||||
self.tx.eq(tx.tx)
|
||||
]
|
||||
return m
|
||||
|
||||
|
||||
class Top(Elaboratable):
|
||||
def __init__(self, firmware, simulate):
|
||||
self.firmware = firmware
|
||||
self.simulate = simulate
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
if self.simulate:
|
||||
io_user_led = Signal()
|
||||
io_uart_tx = Signal()
|
||||
else:
|
||||
cd_sync = ClockDomain(reset_less=True)
|
||||
m.domains += cd_sync
|
||||
m.d.comb += cd_sync.clk.eq(platform.request("clk100").i)
|
||||
io_user_led = platform.request("led").o
|
||||
io_uart_tx = platform.request("uart").tx.o
|
||||
|
||||
counter = Signal(27)
|
||||
m.d.sync += counter.eq(counter + 1)
|
||||
m.d.comb += io_user_led.eq(counter[-1])
|
||||
|
||||
m.submodules.cpu = cpu = Minerva(with_icache=False, with_dcache=False, with_muldiv=False)
|
||||
m.submodules.ram = ram = wishbone.SRAM(Memory(width=32, depth=1024, init=self.firmware))
|
||||
m.submodules.uart = uart = SimpleWishboneSerial(io_uart_tx, 100e6)
|
||||
m.submodules.con = con = wishbone.InterconnectShared(
|
||||
[cpu.ibus, cpu.dbus],
|
||||
[
|
||||
(lambda a: ~a[20], ram.bus),
|
||||
(lambda a: a[20], uart.bus)
|
||||
], register=True)
|
||||
|
||||
return m
|
||||
|
||||
|
||||
def read_firmware(file):
|
||||
firmware = []
|
||||
with open(file, "rb") as f:
|
||||
while True:
|
||||
word = f.read(4)
|
||||
if len(word) < 4:
|
||||
break
|
||||
firmware.append(struct.unpack("<I", word)[0])
|
||||
return firmware
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--simulate", action="store_true")
|
||||
parser.add_argument("firmware_bin")
|
||||
parser.add_argument("build_dir")
|
||||
args = parser.parse_args()
|
||||
|
||||
firmware = read_firmware(args.firmware_bin)
|
||||
top = Top(firmware, args.simulate)
|
||||
|
||||
if args.simulate:
|
||||
os.makedirs(args.build_dir, exist_ok=True)
|
||||
with pysim.Simulator(top,
|
||||
vcd_file=open(os.path.join(args.build_dir, "simplesoc.vcd"), "w"),
|
||||
gtkw_file=open(os.path.join(args.build_dir, "simplesoc.gtkw"), "w")) as sim:
|
||||
sim.add_clock(1e-6)
|
||||
sim.run_until(1000e-6, run_passive=True)
|
||||
else:
|
||||
VersaECP5Platform().build(top, build_dir=args.build_dir)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,11 +1,12 @@
|
|||
{ pkgs, hx }:
|
||||
|
||||
pkgs.runCommand "helloworld-bitstream" {
|
||||
buildInputs = [ (pkgs.python3.withPackages(ps: [hx.nmigen hx.nmigen-boards hx.heavycomps])) pkgs.yosys ];
|
||||
pkgs.runCommand "testing-ecp5-bitstream" {
|
||||
buildInputs = [ (pkgs.python3.withPackages(ps: [hx.nmigen hx.nmigen-boards hx.nmigen-soc hx.heavycomps hx.minerva])) pkgs.yosys ];
|
||||
}
|
||||
''
|
||||
export YOSYS=${pkgs.yosys}/bin/yosys
|
||||
export NEXTPNR_ECP5=${pkgs.nextpnr}/bin/nextpnr-ecp5
|
||||
export ECPPACK=${pkgs.trellis}/bin/ecppack
|
||||
python ${./helloworld_ecp5.py} $out
|
||||
#export NMIGEN_verbose=1
|
||||
python ${./testing_ecp5.py} ${hx.fw-helloworld}/testing.bin $out
|
||||
''
|
|
@ -0,0 +1,97 @@
|
|||
import argparse
|
||||
import struct
|
||||
|
||||
from nmigen import *
|
||||
from nmigen_boards.versa_ecp5 import VersaECP5Platform_USRMCLK
|
||||
|
||||
from heavycomps.cores import *
|
||||
from heavycomps.cfgs.ecp5 import *
|
||||
|
||||
from minerva.core import Minerva
|
||||
from nmigen_soc import wishbone
|
||||
|
||||
from nmigen.utils import *
|
||||
|
||||
|
||||
class Top(Elaboratable):
|
||||
def __init__(self, firmware):
|
||||
self.firmware = firmware
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
# Referring to simplesoc_ecp5.py
|
||||
cd_sync = ClockDomain()
|
||||
m.domains += cd_sync
|
||||
m.d.comb += cd_sync.clk.eq(platform.request("clk100").i)
|
||||
|
||||
io_user_led = [platform.request("led", i).o for i in range(8)]
|
||||
io_uart = platform.request("uart")
|
||||
io_spiflash = platform.request("spi_flash_1x")
|
||||
io_eth = platform.request("eth_rgmii", number=0)
|
||||
|
||||
# FIXME: Without an sync stmt, clk100 must be checked...
|
||||
dummy = Signal()
|
||||
m.d.sync += dummy.eq(0)
|
||||
|
||||
m.submodules.cpu = cpu = Minerva(with_icache=False, with_dcache=False, with_muldiv=False)
|
||||
m.submodules.ram = ram = wishbone.SRAM(Memory(width=32, depth=2**15, init=self.firmware),
|
||||
granularity=8, features={"err","cti","bte"})
|
||||
|
||||
m.submodules.timer = timer = TimerCore(width=32,
|
||||
bus_data_width=32, bus_granularity=8)
|
||||
m.submodules.led = led = GPIOOutput(io_user_led, count=8,
|
||||
bus_data_width=32, bus_granularity=8)
|
||||
m.submodules.uart = uart = UARTCore(io_uart, sys_clk_freq=100e6,
|
||||
bus_data_width=32, bus_granularity=8)
|
||||
m.submodules.spi = spi = SPIFlashCore(io_spiflash, spi_protocol="standard", read_type="slow",
|
||||
addr_width=24, sys_clk_freq=100e6, freq=10e6,
|
||||
data_width=32, granularity=8)
|
||||
m.submodules += SPIFlashCoreCfg(spi)
|
||||
m.submodules.eth = eth = EthRGMIICore(rx=True, tx=False,
|
||||
bus_data_width=32, bus_granularity=8)
|
||||
m.submodules += EthRGMIICoreCfg(eth, io_eth, rx=True, tx=False)
|
||||
|
||||
m.submodules.con = con = wishbone.InterconnectShared(
|
||||
addr_width=30, data_width=32, granularity=8,
|
||||
features={"err","cti","bte"},
|
||||
itors=[cpu.ibus, cpu.dbus],
|
||||
targets=[
|
||||
( ram.bus, 0x00000000 ),
|
||||
( uart.csr_bus, 0x02000000 ),
|
||||
( timer.csr_bus, 0x02000020 ),
|
||||
( led.csr_bus, 0x02000010 ),
|
||||
( spi.bus, 0x03000000 ),
|
||||
( spi.csr_bus, 0x02000100 ),
|
||||
( eth.csr_bus, 0x02000200 ),
|
||||
( eth.rxdat_bus, 0x02000204 )
|
||||
]
|
||||
)
|
||||
|
||||
return m
|
||||
|
||||
|
||||
def read_firmware(file):
|
||||
firmware = []
|
||||
with open(file, "rb") as f:
|
||||
while True:
|
||||
word = f.read(4)
|
||||
if len(word) < 4:
|
||||
break
|
||||
firmware.append(struct.unpack("<I", word)[0])
|
||||
return firmware
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("firmware_bin")
|
||||
parser.add_argument("build_dir")
|
||||
args = parser.parse_args()
|
||||
|
||||
firmware = read_firmware(args.firmware_bin)
|
||||
top = Top(firmware)
|
||||
|
||||
VersaECP5Platform_USRMCLK().build(top, build_dir=args.build_dir)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,5 +0,0 @@
|
|||
MEMORY
|
||||
{
|
||||
FLASH : ORIGIN = 0x00000000, LENGTH = 16K
|
||||
RAM : ORIGIN = 0x0004000, LENGTH = 16K
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate riscv_rt;
|
||||
extern crate panic_halt;
|
||||
|
||||
use riscv_rt::entry;
|
||||
|
||||
fn print(string: &str) {
|
||||
for c in string.chars() {
|
||||
let mem = 0x00400000 as *mut u8;
|
||||
unsafe { *mem = c as u8 }
|
||||
}
|
||||
}
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
loop {
|
||||
print("hello2");
|
||||
}
|
||||
}
|
|
@ -19,6 +19,6 @@ rustPlatform.buildRustPackage rec {
|
|||
installPhase = ''
|
||||
mkdir -p $out
|
||||
cp target/riscv32i-unknown-none-elf/release/helloworld $out
|
||||
${binutils-riscv32}/bin/riscv32-unknown-elf-objcopy -O binary target/riscv32i-unknown-none-elf/release/helloworld $out/helloworld.bin
|
||||
${binutils-riscv32}/bin/riscv32-unknown-elf-objcopy -O binary target/riscv32i-unknown-none-elf/release/helloworld $out/testing.bin
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
MEMORY
|
||||
{
|
||||
FLASH : ORIGIN = 0x00000000, LENGTH = 64K
|
||||
RAM : ORIGIN = 0x00004000, LENGTH = 16K
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
use core::ptr::{read_volatile, write_volatile};
|
||||
|
||||
const ETH_FLAGS : *mut u32 = (0x02000200) as *mut u32;
|
||||
const ETH_RXDATA: *mut u32 = (0x02000204) as *mut u32;
|
||||
|
||||
pub fn eth_get_rxready() -> u8 {
|
||||
unsafe {
|
||||
let full = read_volatile(ETH_FLAGS) as u32;
|
||||
return (full & 0x0001) as u8;
|
||||
}
|
||||
}
|
||||
pub fn eth_enable_rx_clear() {
|
||||
unsafe {
|
||||
let bitmasked = read_volatile(ETH_FLAGS) & (!0x0100) as u32;
|
||||
write_volatile(ETH_FLAGS, (0x0100 | bitmasked) as u32);
|
||||
}
|
||||
}
|
||||
pub fn eth_disable_rx_clear() {
|
||||
unsafe {
|
||||
let bitmasked = read_volatile(ETH_FLAGS) & (!0x0100) as u32;
|
||||
write_volatile(ETH_FLAGS, (0x0000 | bitmasked) as u32);
|
||||
}
|
||||
}
|
||||
pub fn eth_read_rxdat() -> u32 {
|
||||
unsafe {
|
||||
return read_volatile(ETH_RXDATA) as u32;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper functions
|
||||
pub fn eth_get_byte_from_dat(dat: u32) -> u8 {
|
||||
unsafe {
|
||||
return (dat & 0x00FF) as u8;
|
||||
}
|
||||
}
|
||||
pub fn eth_get_eop_from_dat(dat: u32) -> u8 {
|
||||
unsafe {
|
||||
return ((dat & 0x0100) >> 8) as u8;
|
||||
}
|
||||
}
|
||||
pub fn eth_get_nodat_from_dat(dat: u32) -> u8 {
|
||||
unsafe {
|
||||
return ((dat & 0x0200) >> 9) as u8;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
use core::ptr::{read_volatile, write_volatile};
|
||||
|
||||
const GPIO_SWITCH: *mut u32 = (0x02000010) as *mut u32;
|
||||
|
||||
pub fn gpio_led_on(index: u32) {
|
||||
unsafe {
|
||||
let bitmask = 1 << index as u32;
|
||||
let bitmasked = read_volatile(GPIO_SWITCH) & (0xFFFF - bitmask) as u32;
|
||||
write_volatile(GPIO_SWITCH, ((1 << index) | bitmasked) as u32);
|
||||
}
|
||||
}
|
||||
pub fn gpio_led_off(index: u32) {
|
||||
unsafe {
|
||||
let bitmask = 1 << index as u32;
|
||||
let bitmasked = read_volatile(GPIO_SWITCH) & (0xFFFF - bitmask) as u32;
|
||||
write_volatile(GPIO_SWITCH, ((0 << index) | bitmasked) as u32);
|
||||
}
|
||||
}
|
||||
pub fn gpio_led_toggle(index: u32) {
|
||||
unsafe {
|
||||
let raw = read_volatile(GPIO_SWITCH) as u32;
|
||||
let bitmask = 1 << index as u32;
|
||||
let bitmasked = raw & (!bitmask) as u32;
|
||||
let current = raw & bitmask as u32;
|
||||
write_volatile(GPIO_SWITCH, (!current & bitmask | bitmasked) as u32);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,310 @@
|
|||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
extern crate riscv_rt;
|
||||
extern crate panic_halt;
|
||||
|
||||
use riscv_rt::entry;
|
||||
|
||||
mod uart;
|
||||
mod gpio;
|
||||
mod timer;
|
||||
mod spiflash;
|
||||
mod eth;
|
||||
|
||||
#[entry]
|
||||
fn main() -> ! {
|
||||
uart::uart_enable_rx();
|
||||
println!("");
|
||||
println!(" * * HeavyX Testing Firmware * *");
|
||||
println!("");
|
||||
|
||||
loop {
|
||||
println!("--------------------------------------------------");
|
||||
println!("");
|
||||
println!(" [Main Menu]");
|
||||
println!("");
|
||||
println!(" Type a number to select function:");
|
||||
println!(" [1] UART Typewriter");
|
||||
println!(" [2] SPI Flash Reader");
|
||||
println!(" [3] Ethernet Packet Viewer");
|
||||
println!(" [4] LED Blinker");
|
||||
print!(">> ");
|
||||
loop {
|
||||
if uart::uart_get_rxready() == 1 {
|
||||
let c = uart::uart_read_byte() as u8;
|
||||
if c == b'1' { print!("{}\n", c as char); uart_test(); break; }
|
||||
if c == b'2' { print!("{}\n", c as char); spi_test(); break; }
|
||||
if c == b'3' { print!("{}\n", c as char); eth_test(); break; }
|
||||
if c == b'4' { print!("{}\n", c as char); blink_test(); break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn uart_test() {
|
||||
println!("--------------------------------------------------");
|
||||
println!("");
|
||||
println!(" [UART Typewriter]");
|
||||
println!("");
|
||||
println!(" Type to start writing.");
|
||||
println!(" Backspacing is disabled.");
|
||||
println!(" Press [Ctrl]+[D] to exit.");
|
||||
println!("..................................................");
|
||||
loop {
|
||||
if uart::uart_get_rxready() == 1 {
|
||||
let c = uart::uart_read_byte() as u8;
|
||||
if c == b'\r' { print!("\n") }
|
||||
else if c == 0x04 { print!("\n"); break; }
|
||||
else if c > 0x1f && c != 0x7f { print!("{}", c as char) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn spi_test() {
|
||||
println!("--------------------------------------------------");
|
||||
println!("");
|
||||
println!(" [SPI Flash Reader]");
|
||||
println!("");
|
||||
println!(" Press - / + to change start address.");
|
||||
println!(" Press [ / ] to adjust read size.");
|
||||
println!(" Press , / . to adjust SPI divisor.");
|
||||
println!(" Press [Enter] to read.");
|
||||
println!(" Press [Ctrl]+[D] to exit.");
|
||||
let mut spi_read_addr = 0x000000 as u32;
|
||||
let mut spi_read_size = 512 as u32;
|
||||
loop {
|
||||
let mut spi_div = spiflash::flash_get_div() as u32;
|
||||
println!(">> [ 0x{:06x} - 0x{:06x} ({} bytes) @ divisor = {} ]",
|
||||
spi_read_addr, spi_read_addr + spi_read_size - 1, spi_read_size, spi_div);
|
||||
let mut spi_exit = 0 as u8;
|
||||
loop {
|
||||
if uart::uart_get_rxready() == 1 {
|
||||
let c = uart::uart_read_byte() as u8;
|
||||
// Change address
|
||||
if c == b'-' || c == b'+' {
|
||||
if c == b'-' && spi_read_addr > 0 {
|
||||
if spi_read_addr < spi_read_size { spi_read_addr = 0 }
|
||||
else { spi_read_addr -= spi_read_size }
|
||||
}
|
||||
if c == b'+' && spi_read_addr < 0xffffff {
|
||||
if spi_read_addr >= 0x1000000 - spi_read_size { spi_read_addr = 0x1000000 - spi_read_size}
|
||||
else { spi_read_addr += spi_read_size }
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Change read size
|
||||
if c == b'[' || c == b']' {
|
||||
if c == b'[' && spi_read_size > 16 {
|
||||
spi_read_size >>= 1
|
||||
}
|
||||
if c == b']' && spi_read_addr + (spi_read_size << 1) <= 0x1000000 {
|
||||
spi_read_size <<= 1
|
||||
}
|
||||
break;
|
||||
}
|
||||
// Change divisor
|
||||
if c == b',' || c == b'.' {
|
||||
if c == b',' && spi_div > 1 { spiflash::flash_set_div(spi_div-1) }
|
||||
if c == b'.' && spi_div < 0xffff { spiflash::flash_set_div(spi_div+1) }
|
||||
break;
|
||||
}
|
||||
// Read
|
||||
if c == b'\r' {
|
||||
for x in (spi_read_addr..(spi_read_addr+spi_read_size)).step_by(16) {
|
||||
println!(" 0x{:06x} : {:08x} {:08x} {:08x} {:08x}", x,
|
||||
spiflash::flash_read_word(x), spiflash::flash_read_word(x+4),
|
||||
spiflash::flash_read_word(x+8), spiflash::flash_read_word(x+12));
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if c == 0x04 { spi_exit = 1; break; }
|
||||
}
|
||||
}
|
||||
if spi_exit == 1 { break; }
|
||||
}
|
||||
}
|
||||
|
||||
fn eth_test() {
|
||||
println!("--------------------------------------------------");
|
||||
println!("");
|
||||
println!(" [Ethernet Packet Viewer]");
|
||||
println!("");
|
||||
println!(" Automatically receiving incoming packets ...");
|
||||
println!(" Press [Ctrl]+[D] to exit.");
|
||||
println!("");
|
||||
let mut eth_rx_count = 0 as u32;
|
||||
let mut eth_rx_start = 1 as u32;
|
||||
loop {
|
||||
let mut j = 0;
|
||||
let mut byte_count = 0;
|
||||
let mut eth_exit = 0 as u8;
|
||||
loop {
|
||||
if eth::eth_get_rxready() == 1 {
|
||||
// Read a byte from the packet
|
||||
let rxdat = eth::eth_read_rxdat();
|
||||
// If reading returns no data, accept UART interrupt
|
||||
if eth::eth_get_nodat_from_dat(rxdat) == 1 && eth_rx_start == 1 {
|
||||
timer::timer_set(5000000, 0);
|
||||
timer::timer_enable();
|
||||
while timer::timer_get() != 0 {
|
||||
if uart::uart_get_rxready() == 1 {
|
||||
let c = uart::uart_read_byte() as u8;
|
||||
if c == 0x04 { eth_exit = 1; timer::timer_disable(); break; }
|
||||
}
|
||||
}
|
||||
timer::timer_disable();
|
||||
}
|
||||
// Otherwise, return byte
|
||||
else {
|
||||
eth_rx_start = 0;
|
||||
if byte_count == 0 {
|
||||
println!(">> New eth packet # {}:", eth_rx_count);
|
||||
}
|
||||
if j == 0 { print!(" ") }
|
||||
if j == 8 { print!(" ") }
|
||||
print!("{:02x} ", eth::eth_get_byte_from_dat(rxdat));
|
||||
byte_count += 1;
|
||||
if eth::eth_get_eop_from_dat(rxdat) == 1 {
|
||||
print!("\n");
|
||||
j = 0;
|
||||
eth_rx_start = 1;
|
||||
eth_rx_count += 1;
|
||||
break;
|
||||
}
|
||||
if j == 15 { print!("\n"); j = 0; }
|
||||
else { j += 1; }
|
||||
}
|
||||
}
|
||||
else {
|
||||
println!("\n>> Buffer is full, clearing current buffer...");
|
||||
eth::eth_enable_rx_clear();
|
||||
while eth::eth_get_rxready() == 0 {
|
||||
if uart::uart_get_rxready() == 1 {
|
||||
let c = uart::uart_read_byte() as u8;
|
||||
if c == 0x04 { eth_exit = 1; timer::timer_disable(); break; }
|
||||
}
|
||||
}
|
||||
eth::eth_disable_rx_clear();
|
||||
println!(">> Buffer cleared. Abandoned eth packet # {}:", eth_rx_count);
|
||||
j = 0;
|
||||
byte_count = 0;
|
||||
eth_rx_count += 1;
|
||||
}
|
||||
if eth_exit == 1 { break; }
|
||||
}
|
||||
if eth_exit == 1 { break; }
|
||||
}
|
||||
}
|
||||
|
||||
fn blink_test() {
|
||||
println!("--------------------------------------------------");
|
||||
println!("");
|
||||
println!(" [LED Blinker]");
|
||||
println!("");
|
||||
println!(" Each LED will blink once first (primary),");
|
||||
println!(" and then twice (secondary).");
|
||||
println!(" Press - / + to adjust primary blink time.");
|
||||
println!(" Press , / . to adjust secondary blink time.");
|
||||
println!(" Press [Ctrl]+[D] to stop and exit.");
|
||||
println!("");
|
||||
for i in 0..8 {
|
||||
gpio::gpio_led_off(i);
|
||||
}
|
||||
let mut timer_load_primary = 50000000 as u32;
|
||||
let mut timer_load_secondary = 12500000 as u32;
|
||||
println!(">> [ Primary @ {} clocks ]", timer_load_primary);
|
||||
println!(" [ Secondary @ {} clocks ]", timer_load_secondary);
|
||||
let mut blink_exit = 0 as u8;
|
||||
loop {
|
||||
for i in 0..8 {
|
||||
println!(">> Blinking LED #{}", i+1);
|
||||
timer::timer_set(timer_load_primary, 0);
|
||||
timer::timer_enable();
|
||||
gpio::gpio_led_toggle(i);
|
||||
while timer::timer_get() != 0 {
|
||||
if uart::uart_get_rxready() == 1 {
|
||||
let c = uart::uart_read_byte() as u8;
|
||||
if c == b'-' || c == b'+' {
|
||||
if c == b'-' && timer_load_primary > 5000000 { timer_load_primary >>= 1 }
|
||||
if c == b'+' && timer_load_primary < 0xffffffff { timer_load_primary <<= 1 }
|
||||
}
|
||||
if c == b',' || c == b'.' {
|
||||
if c == b',' && timer_load_secondary > 5000000 { timer_load_secondary >>= 1 }
|
||||
if c == b'.' && timer_load_secondary < 0xffffffff { timer_load_secondary <<= 1 }
|
||||
}
|
||||
else if c == 0x04 { blink_exit = 1; timer::timer_disable(); break; }
|
||||
println!(">> [ Primary @ {} clocks ]", timer_load_primary);
|
||||
println!(" [ Secondary @ {} clocks ]", timer_load_secondary);
|
||||
}
|
||||
}
|
||||
timer::timer_disable(); // Reset to LOAD
|
||||
timer::timer_enable();
|
||||
gpio::gpio_led_toggle(i);
|
||||
while timer::timer_get() != 0 {
|
||||
if uart::uart_get_rxready() == 1 {
|
||||
let c = uart::uart_read_byte() as u8;
|
||||
if c == b'-' || c == b'+' {
|
||||
if c == b'-' && timer_load_primary > 5000000 { timer_load_primary >>= 1 }
|
||||
if c == b'+' && timer_load_primary < 0xffffffff { timer_load_primary <<= 1 }
|
||||
}
|
||||
if c == b',' || c == b'.' {
|
||||
if c == b',' && timer_load_secondary > 5000000 { timer_load_secondary >>= 1 }
|
||||
if c == b'.' && timer_load_secondary < 0xffffffff { timer_load_secondary <<= 1 }
|
||||
}
|
||||
else if c == 0x04 { blink_exit = 1; timer::timer_disable(); break; }
|
||||
println!(">> [ Primary @ {} clocks ]", timer_load_primary);
|
||||
println!(" [ Secondary @ {} clocks ]", timer_load_secondary);
|
||||
}
|
||||
}
|
||||
timer::timer_disable(); // Reset to LOAD
|
||||
for __ in 0..2 {
|
||||
timer::timer_set(timer_load_secondary, 0);
|
||||
timer::timer_enable();
|
||||
gpio::gpio_led_toggle(i);
|
||||
while timer::timer_get() != 0 {
|
||||
if uart::uart_get_rxready() == 1 {
|
||||
let c = uart::uart_read_byte() as u8;
|
||||
if c == b'-' || c == b'+' {
|
||||
if c == b'-' && timer_load_primary > 5000000 { timer_load_primary >>= 1 }
|
||||
if c == b'+' && timer_load_primary < 0xffffffff { timer_load_primary <<= 1 }
|
||||
}
|
||||
if c == b',' || c == b'.' {
|
||||
if c == b',' && timer_load_secondary > 5000000 { timer_load_secondary >>= 1 }
|
||||
if c == b'.' && timer_load_secondary < 0xffffffff { timer_load_secondary <<= 1 }
|
||||
}
|
||||
else if c == 0x04 { blink_exit = 1; timer::timer_disable(); break; }
|
||||
println!(">> [ Primary @ {} clocks ]", timer_load_primary);
|
||||
println!(" [ Secondary @ {} clocks ]", timer_load_secondary);
|
||||
}
|
||||
}
|
||||
timer::timer_disable(); // Reset to LOAD
|
||||
timer::timer_enable();
|
||||
gpio::gpio_led_toggle(i);
|
||||
while timer::timer_get() != 0 {
|
||||
if uart::uart_get_rxready() == 1 {
|
||||
let c = uart::uart_read_byte() as u8;
|
||||
if c == b'-' || c == b'+' {
|
||||
if c == b'-' && timer_load_primary > 5000000 { timer_load_primary >>= 1 }
|
||||
if c == b'+' && timer_load_primary < 0xffffffff { timer_load_primary <<= 1 }
|
||||
}
|
||||
if c == b',' || c == b'.' {
|
||||
if c == b',' && timer_load_secondary > 5000000 { timer_load_secondary >>= 1 }
|
||||
if c == b'.' && timer_load_secondary < 0xffffffff { timer_load_secondary <<= 1 }
|
||||
}
|
||||
else if c == 0x04 { blink_exit = 1; timer::timer_disable(); break; }
|
||||
println!(">> [ Primary @ {} clocks ]", timer_load_primary);
|
||||
println!(" [ Secondary @ {} clocks ]", timer_load_secondary);
|
||||
}
|
||||
}
|
||||
timer::timer_disable(); // Reset to LOAD
|
||||
}
|
||||
gpio::gpio_led_off(i); // Turn off the current LED
|
||||
if blink_exit == 1 { break; }
|
||||
}
|
||||
if blink_exit == 1 { break; }
|
||||
}
|
||||
for i in 0..8 {
|
||||
gpio::gpio_led_off(i);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
use core::ptr::{read_volatile, write_volatile};
|
||||
|
||||
const FLASH_CONTROL: *mut u32 = (0x02000100) as *mut u32;
|
||||
const FLASH_START_ADDR: u32 = (0x03000000);
|
||||
|
||||
pub fn flash_set_div(div: u32) {
|
||||
unsafe {
|
||||
write_volatile(FLASH_CONTROL, div as u32);
|
||||
}
|
||||
}
|
||||
pub fn flash_get_div() -> u32 {
|
||||
unsafe {
|
||||
return read_volatile(FLASH_CONTROL) as u32
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flash_read_word(byte_offset: u32) -> u32 {
|
||||
let mem = (FLASH_START_ADDR + byte_offset) as *mut u32;
|
||||
unsafe {
|
||||
return *mem
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
use core::ptr::{read_volatile, write_volatile};
|
||||
|
||||
const TIMER_CONTROL: *mut u32 = (0x02000020) as *mut u32;
|
||||
const TIMER_LOAD : *mut u32 = (0x02000024) as *mut u32;
|
||||
const TIMER_RELOAD : *mut u32 = (0x02000028) as *mut u32;
|
||||
const TIMER_CURRENT: *mut u32 = (0x0200002C) as *mut u32;
|
||||
|
||||
pub fn timer_enable() {
|
||||
unsafe {
|
||||
let bitmasked = read_volatile(TIMER_CONTROL) & (!0x11) as u32;
|
||||
write_volatile(TIMER_CONTROL, (0x11 | bitmasked) as u32);
|
||||
}
|
||||
}
|
||||
pub fn timer_disable() {
|
||||
unsafe {
|
||||
let bitmasked = read_volatile(TIMER_CONTROL) & (!0x11) as u32;
|
||||
write_volatile(TIMER_CONTROL, (0x00 | bitmasked) as u32);
|
||||
}
|
||||
}
|
||||
pub fn timer_set(load: u32, reload: u32) {
|
||||
unsafe {
|
||||
write_volatile(TIMER_LOAD, load as u32);
|
||||
write_volatile(TIMER_RELOAD, reload as u32);
|
||||
}
|
||||
}
|
||||
pub fn timer_get() -> u32 {
|
||||
unsafe {
|
||||
return read_volatile(TIMER_CURRENT) as u32
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
use core::ptr::{read_volatile, write_volatile};
|
||||
use core::fmt;
|
||||
|
||||
const UART_CONTROL: *mut u32 = (0x02000000) as *mut u32;
|
||||
const UART_FLAGS : *mut u32 = (0x02000004) as *mut u32;
|
||||
const UART_TXDATA : *mut u32 = (0x02000008) as *mut u32;
|
||||
const UART_RXDATA : *mut u32 = (0x0200000C) as *mut u32;
|
||||
|
||||
pub fn uart_set_div(div: u32) {
|
||||
unsafe {
|
||||
write_volatile(UART_CONTROL, div as u32);
|
||||
}
|
||||
}
|
||||
pub fn uart_get_div() -> u32 {
|
||||
unsafe {
|
||||
return read_volatile(UART_CONTROL) as u32
|
||||
}
|
||||
}
|
||||
pub fn uart_enable_rx() {
|
||||
unsafe {
|
||||
let bitmasked = read_volatile(UART_FLAGS) & (!0x0100) as u32;
|
||||
write_volatile(UART_FLAGS, (0x0100 | bitmasked) as u32);
|
||||
}
|
||||
}
|
||||
pub fn uart_disable_rx() {
|
||||
unsafe {
|
||||
let bitmasked = read_volatile(UART_FLAGS) & (!0x0100) as u32;
|
||||
write_volatile(UART_FLAGS, (0x0000 | bitmasked) as u32);
|
||||
}
|
||||
}
|
||||
pub fn uart_get_rxready() -> u8 {
|
||||
unsafe {
|
||||
let full = read_volatile(UART_FLAGS) as u32;
|
||||
return (full & 0x0001) as u8;
|
||||
}
|
||||
}
|
||||
pub fn uart_read_byte() -> u8 {
|
||||
unsafe {
|
||||
return read_volatile(UART_RXDATA) as u8;
|
||||
}
|
||||
}
|
||||
pub fn uart_get_txready() -> u8 {
|
||||
unsafe {
|
||||
let full = read_volatile(UART_FLAGS) as u32;
|
||||
return ((full & 0x0010) >> 4) as u8;
|
||||
}
|
||||
}
|
||||
pub fn uart_write_byte(c: u8) {
|
||||
unsafe {
|
||||
write_volatile(UART_TXDATA, c as u32);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Console;
|
||||
|
||||
impl fmt::Write for Console {
|
||||
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
|
||||
for c in s.bytes() {
|
||||
while uart_get_txready() == 0 {}
|
||||
uart_write_byte(c);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => ({
|
||||
use core::fmt::Write;
|
||||
write!($crate::uart::Console, $($arg)*).unwrap()
|
||||
})
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
($fmt:expr) => (print!(concat!($fmt, "\n")));
|
||||
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
{ stdenv, python3Packages, nmigen }:
|
||||
{ stdenv, python3Packages, nmigen, nmigen-stdio, nmigen-soc }:
|
||||
|
||||
python3Packages.buildPythonPackage {
|
||||
name = "heavycomps";
|
||||
|
||||
src = ./heavycomps;
|
||||
|
||||
propagatedBuildInputs = [ nmigen ];
|
||||
propagatedBuildInputs = [ nmigen nmigen-stdio nmigen-soc ];
|
||||
|
||||
meta = with stdenv.lib; {
|
||||
description = "Components for the HeavyX SoC toolkit";
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
from nmigen import *
|
||||
|
||||
|
||||
__all__ = ["EthRGMIICoreCfg", "SPIFlashCoreCfg"]
|
||||
|
||||
|
||||
class EthRGMIICoreCfg(Elaboratable):
|
||||
def __init__(self, core, eth_pins, *, rx, tx, tx_clk=None):
|
||||
self.core = core
|
||||
self.eth_pins = eth_pins
|
||||
if not isinstance(rx, bool):
|
||||
raise TypeError("Must include either rx=True or False "
|
||||
"to indicate whether RX is used or not, not rx={}"
|
||||
.format(repr(rx)))
|
||||
self.rx = rx
|
||||
if not isinstance(tx, bool):
|
||||
raise TypeError("Must include either tx=True or False "
|
||||
"to indicate whether TX is used or not, not tx={}"
|
||||
.format(repr(tx)))
|
||||
self.tx = tx
|
||||
|
||||
if tx:
|
||||
if tx_clk is None:
|
||||
raise TypeError("Signal as TX clock must not be None")
|
||||
self.tx_clk = tx_clk
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
if self.rx:
|
||||
rx = self.core.rx_io
|
||||
# DDRInput on rx_ctl
|
||||
m.submodules += [
|
||||
Instance("IDDRX1F",
|
||||
i_D=self.eth_pins.rx_ctl,
|
||||
i_SCLK=rx.rx_ctl_iddr.i_clk,
|
||||
o_Q0=rx.rx_ctl_iddr.i0,
|
||||
o_Q1=rx.rx_ctl_iddr.i1,
|
||||
)
|
||||
]
|
||||
# DDRInput on rx_data
|
||||
for i in range(4):
|
||||
m.submodules += [
|
||||
Instance("IDDRX1F",
|
||||
i_D=self.eth_pins.rx_data[i],
|
||||
i_SCLK=rx.rx_data_iddr.i_clk,
|
||||
o_Q0=rx.rx_data_iddr.i0[i],
|
||||
o_Q1=rx.rx_data_iddr.i1[i],
|
||||
)
|
||||
]
|
||||
# RX Clock
|
||||
cd_eth_rx = ClockDomain("eth_rx")
|
||||
m.domains += cd_eth_rx
|
||||
m.d.comb += cd_eth_rx.clk.eq(self.eth_pins.rx_clk)
|
||||
|
||||
if self.tx:
|
||||
tx = self.core.tx_io
|
||||
# DDROutput on tx_clk
|
||||
m.submodules += [
|
||||
Instance("ODDRX1F",
|
||||
i_D0=tx.tx_clk_oddr.o0,
|
||||
i_D1=tx.tx_clk_oddr.o1,
|
||||
i_SCLK=tx.tx_clk_oddr.o_clk,
|
||||
o_Q=self.eth_pins.tx_clk
|
||||
)
|
||||
]
|
||||
# DDROutput on tx_ctl
|
||||
m.submodules += [
|
||||
Instance("ODDRX1F",
|
||||
i_D0=tx.tx_ctl_oddr.o0,
|
||||
i_D1=tx.tx_ctl_oddr.o1,
|
||||
i_SCLK=tx.tx_ctl_oddr.o_clk,
|
||||
o_Q=self.eth_pins.tx_ctl
|
||||
)
|
||||
]
|
||||
# DDROutput on tx_data
|
||||
for i in range(4):
|
||||
m.submodules += [
|
||||
Instance("ODDRX1F",
|
||||
i_D0=tx.tx_data_oddr.o0[i],
|
||||
i_D1=tx.tx_data_oddr.o1[i],
|
||||
i_SCLK=tx.tx_data_oddr.o_clk,
|
||||
o_Q=self.eth_pins.tx_data[i]
|
||||
)
|
||||
]
|
||||
# TX Clock
|
||||
cd_eth_tx = ClockDomain("eth_tx")
|
||||
m.domains += cd_eth_tx
|
||||
m.d.comb += cd_eth_tx.clk.eq(self.tx_clk)
|
||||
|
||||
return m
|
||||
|
||||
|
||||
class SPIFlashCoreCfg(Elaboratable):
|
||||
def __init__(self, core):
|
||||
self.core = core
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
spi = self.core.spi_io
|
||||
m.submodules += Instance("USRMCLK",
|
||||
i_USRMCLKI=spi.clk,
|
||||
i_USRMCLKTS=0)
|
||||
|
||||
return m
|
|
@ -0,0 +1,5 @@
|
|||
from .uart import *
|
||||
from .gpio import *
|
||||
from .timer import *
|
||||
from .spiflash import *
|
||||
from .eth_rgmii import *
|
|
@ -0,0 +1,159 @@
|
|||
from nmigen import *
|
||||
from nmigen_stdio.eth import rgmii
|
||||
from nmigen_soc import csr, wishbone
|
||||
from nmigen.utils import *
|
||||
|
||||
from nmigen.lib.fifo import AsyncFIFO
|
||||
from nmigen.lib.cdc import FFSynchronizer, ResetSynchronizer
|
||||
|
||||
|
||||
__all__ = ["EthRGMIICore"]
|
||||
|
||||
|
||||
class EthRGMIICore(Elaboratable):
|
||||
def __init__(self, *, rx, tx, bus_data_width,
|
||||
rx_buffer_size=2**15, tx_buffer_size=2**15,
|
||||
bus_type="wishbone", bus_granularity=None, **kwargs):
|
||||
if not isinstance(rx, bool):
|
||||
raise TypeError("Must include either rx=True or False "
|
||||
"to indicate whether RX is used or not, not rx={}"
|
||||
.format(repr(rx)))
|
||||
self.rx = rx
|
||||
if not isinstance(tx, bool):
|
||||
raise TypeError("Must include either tx=True or False "
|
||||
"to indicate whether TX is used or not, not tx={}"
|
||||
.format(repr(tx)))
|
||||
self.tx = tx
|
||||
if bus_data_width < 9:
|
||||
raise ValueError("Bus data width must be at least 10, not {}"
|
||||
.format(bus_data_width))
|
||||
|
||||
if bus_type not in ["wishbone"]:
|
||||
raise ValueError("Bus type must be "
|
||||
"\"wishbone\", not {}".format(bus_type))
|
||||
self.bus_type = bus_type
|
||||
if bus_type == "wishbone":
|
||||
self.rxdat_bus = wishbone.Interface(
|
||||
addr_width=0,
|
||||
data_width=bus_data_width,
|
||||
granularity=bus_granularity,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
if rx:
|
||||
self.rx_buffer_size = rx_buffer_size
|
||||
self.rx_stb = Signal()
|
||||
self.rx_eop = Signal()
|
||||
self.rx_io = rgmii.EthRGMIIRX(clk_domain="eth_rx")
|
||||
|
||||
if tx:
|
||||
self.tx_buffer_size = tx_buffer_size
|
||||
self.tx_stb = Signal()
|
||||
self.tx_eop = Signal()
|
||||
self.tx_io = rgmii.EthRGMIITX(clk_domain="eth_tx")
|
||||
|
||||
bus_dw = bus_data_width
|
||||
if bus_granularity is None:
|
||||
bus_granularity = data_width
|
||||
bus_gr = bus_granularity
|
||||
|
||||
with csr.Bank(name="eth", addr_width=max(1, log2_int(bus_dw//bus_gr)),
|
||||
data_width=bus_gr, type="mux") as self.csr:
|
||||
self.csr.r += csr.Register("flags", "rw", width=bus_dw)
|
||||
with self.csr.r.flags as reg:
|
||||
if rx:
|
||||
reg.f += [
|
||||
csr.Field("rx_ready", "r", startbit=0, reset_value=1),
|
||||
csr.Field("rx_clear", "rw", startbit=8),
|
||||
]
|
||||
if tx:
|
||||
reg += [
|
||||
csr.Field("tx_ready", "r", startbit=4, reset_value=1),
|
||||
csr.Field("tx_send", "rw", startbit=12)
|
||||
]
|
||||
self.wb2csr = csr.WishboneCSRBridge(self.csr.mux.bus, data_width=bus_data_width)
|
||||
self.csr_bus = self.wb2csr.wb_bus
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
if self.rx:
|
||||
m.submodules.rx = rx = self.rx_io
|
||||
if self.tx:
|
||||
m.submodules.tx = tx = self.tx_io
|
||||
|
||||
m.submodules += self.csr, self.wb2csr
|
||||
|
||||
###########
|
||||
# RX
|
||||
if self.rx:
|
||||
# RX FIFO ([7:0]=data ; [8]=is eop?)
|
||||
rx_fifo = AsyncFIFO(width=9, depth=self.rx_buffer_size)
|
||||
m.submodules += DomainRenamer({"write": "eth_rx", "read": "sync"})(rx_fifo)
|
||||
rx_clearing = Signal()
|
||||
m.d.comb += [
|
||||
# Write to FIFO
|
||||
# RX stops when clearing buffer
|
||||
rx_fifo.w_en.eq(rx.source.stb & ~rx_clearing),
|
||||
rx_fifo.w_data.eq(Cat(rx.source.payload.data, rx.source.eop)),
|
||||
]
|
||||
|
||||
if self.bus_type == "wishbone":
|
||||
# Reading while clearing buffer would return invalid data
|
||||
with m.If(~rx_clearing):
|
||||
rxdat_r_en = Signal()
|
||||
m.d.comb += [
|
||||
rxdat_r_en.eq(self.rxdat_bus.cyc & self.rxdat_bus.stb & ~self.rxdat_bus.we &
|
||||
~self.rxdat_bus.ack & rx_fifo.r_rdy)
|
||||
]
|
||||
# Read from FIFO
|
||||
m.d.sync += rx_fifo.r_en.eq(rxdat_r_en)
|
||||
with m.If(self.rxdat_bus.sel[0] & rxdat_r_en):
|
||||
m.d.sync += self.rxdat_bus.dat_r.eq(rx_fifo.r_data)
|
||||
with m.Else():
|
||||
m.d.sync += self.rxdat_bus.dat_r.eq(Cat(Repl(0, 9), 1)) # [9]=no data?
|
||||
# RX data bus ACK
|
||||
with m.If(self.rxdat_bus.cyc & self.rxdat_bus.stb & ~self.rxdat_bus.we):
|
||||
m.d.sync += self.rxdat_bus.ack.eq(1)
|
||||
with m.If(self.rxdat_bus.ack):
|
||||
m.d.sync += self.rxdat_bus.ack.eq(0)
|
||||
|
||||
# CSR - Buffer is ready
|
||||
# Set to 0 once buffer is full
|
||||
# Resets to 1 only when buffer has been completely cleared
|
||||
rx_ready_prev = Signal(reset=1)
|
||||
m.d.comb += self.csr.r.flags.f.rx_ready.set_stb.eq(1)
|
||||
with m.If(~rx_fifo.w_rdy):
|
||||
m.d.comb += self.csr.r.flags.f.rx_ready.set_val.eq(0)
|
||||
m.d.sync += rx_ready_prev.eq(0)
|
||||
with m.Else():
|
||||
m.d.comb += self.csr.r.flags.f.rx_ready.set_val.eq(rx_ready_prev)
|
||||
with m.If(~rx_ready_prev & ~rx_fifo.r_rdy):
|
||||
m.d.sync += rx_ready_prev.eq(1)
|
||||
|
||||
# CSR - Buffer clearing
|
||||
# Starts clearing after flag is set to 1
|
||||
# Ignores flag during clearing
|
||||
with m.If(self.csr.r.flags.f.rx_clear.s & ~rx_clearing & rx_fifo.r_rdy):
|
||||
m.d.sync += [
|
||||
rx_fifo.r_en.eq(1),
|
||||
rx_clearing.eq(1)
|
||||
]
|
||||
with m.If(rx_clearing & ~rx_fifo.r_rdy):
|
||||
m.d.sync += [
|
||||
rx_fifo.r_en.eq(0),
|
||||
rx_clearing.eq(0)
|
||||
]
|
||||
|
||||
# Useful signals
|
||||
m.submodules += [
|
||||
FFSynchronizer(rx.source.stb, self.rx_stb),
|
||||
FFSynchronizer(rx.source.eop, self.rx_eop),
|
||||
]
|
||||
|
||||
###########
|
||||
# TX
|
||||
if self.tx:
|
||||
raise NotImplementedError("TX logic has not been implemented")
|
||||
|
||||
return m
|
|
@ -0,0 +1,43 @@
|
|||
from nmigen import *
|
||||
from nmigen_soc import csr
|
||||
from nmigen.utils import *
|
||||
|
||||
|
||||
__all__ = ["GPIOOutput"]
|
||||
|
||||
|
||||
class GPIOOutput(Elaboratable):
|
||||
def __init__(self, gpio_out, *, bus_data_width, count=8, bus_granularity=None):
|
||||
self.gpio_out = gpio_out
|
||||
self.count = count
|
||||
|
||||
bus_dw = bus_data_width
|
||||
if bus_granularity is None:
|
||||
bus_granularity = data_width
|
||||
bus_gr = bus_granularity
|
||||
|
||||
with csr.Bank(name="gpio",
|
||||
addr_width=max(1, log2_int(-(-count//bus_gr), need_pow2=False)),
|
||||
data_width=bus_gr, type="mux") as self.csr:
|
||||
self.csr.r += [
|
||||
csr.Register(
|
||||
"switch", "rw", width=count,
|
||||
fields=[
|
||||
csr.Field("output_{}".format(i)) for i in range(count)
|
||||
]
|
||||
)
|
||||
]
|
||||
self.wb2csr = csr.WishboneCSRBridge(self.csr.mux.bus, data_width=bus_data_width)
|
||||
self.csr_bus = self.wb2csr.wb_bus
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
m.submodules += self.csr, self.wb2csr
|
||||
|
||||
m.d.comb += [
|
||||
self.gpio_out[i].eq(self.csr.r.switch.s[i])
|
||||
for i in range(self.count)
|
||||
]
|
||||
|
||||
return m
|
|
@ -0,0 +1,94 @@
|
|||
from nmigen import *
|
||||
from nmigen_stdio import spiflash
|
||||
from nmigen_soc import csr, wishbone
|
||||
from nmigen.utils import *
|
||||
|
||||
|
||||
__all__ = ["SPIFlashCore"]
|
||||
|
||||
|
||||
class SPIFlashCore(Elaboratable):
|
||||
def __init__(self, spi_pins, *, spi_protocol, read_type,
|
||||
addr_width, sys_clk_freq, freq,
|
||||
data_width=32, divisor_bits=32,
|
||||
bus_type="wishbone", granularity=None, **kwargs):
|
||||
self.spi_pins = spi_pins
|
||||
self.div = int(sys_clk_freq/freq) - 1
|
||||
self.div_bits = divisor_bits
|
||||
|
||||
if read_type not in ["slow", "fast"]:
|
||||
raise ValueError("Type of the read operation must be one of "
|
||||
"\"slow\" or \"fast\", not {}".format(read_type))
|
||||
if read_type == "slow":
|
||||
self.spi_io = spiflash.SPIFlashSlowReader(
|
||||
protocol=spi_protocol,
|
||||
addr_width=addr_width,
|
||||
data_width=data_width,
|
||||
divisor_bits=divisor_bits,
|
||||
divisor=self.div,
|
||||
pins=self.spi_pins
|
||||
)
|
||||
elif read_type == "fast":
|
||||
self.spi_io = spiflash.SPIFlashFastReader(
|
||||
protocol=spi_protocol,
|
||||
addr_width=addr_width,
|
||||
data_width=data_width,
|
||||
divisor_bits=divisor_bits,
|
||||
divisor=self.div,
|
||||
pins=self.spi_pins
|
||||
)
|
||||
|
||||
if bus_type not in ["wishbone"]:
|
||||
raise ValueError("Bus type must be "
|
||||
"\"wishbone\", not {}".format(bus_type))
|
||||
self.bus_type = bus_type
|
||||
if bus_type == "wishbone":
|
||||
if granularity is None:
|
||||
granularity = data_width
|
||||
self.bus = wishbone.Interface(
|
||||
addr_width=addr_width - log2_int(data_width//granularity),
|
||||
data_width=data_width,
|
||||
granularity=granularity,
|
||||
**kwargs
|
||||
)
|
||||
|
||||
bus_dw = data_width
|
||||
if granularity is None:
|
||||
granularity = data_width
|
||||
self.granularity = bus_gr = granularity
|
||||
|
||||
with csr.Bank(name="spi", addr_width=max(1, log2_int(bus_dw//bus_gr)),
|
||||
data_width=bus_gr, type="mux") as self.csr:
|
||||
self.csr.r += [
|
||||
csr.Register(
|
||||
"control", "rw", width=bus_dw,
|
||||
fields=[csr.Field("divisor", "rw", width=self.div_bits, reset_value=self.div)]
|
||||
)
|
||||
]
|
||||
self.wb2csr = csr.WishboneCSRBridge(self.csr.mux.bus, data_width=data_width)
|
||||
self.csr_bus = self.wb2csr.wb_bus
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
m.submodules.spi = spi_io = self.spi_io
|
||||
m.submodules += self.csr, self.wb2csr
|
||||
|
||||
# CSR - Divisor
|
||||
m.d.comb += spi_io.divisor.eq(self.csr.r.control.f.divisor.s)
|
||||
|
||||
# non-CSR - Return Memory
|
||||
if self.bus_type == "wishbone":
|
||||
m.d.comb += [
|
||||
spi_io.ack.eq(self.bus.cyc & self.bus.stb & ~self.bus.we
|
||||
& spi_io.rdy),
|
||||
spi_io.addr.eq(self.bus.adr),
|
||||
self.bus.ack.eq(spi_io.r_rdy)
|
||||
]
|
||||
for i, sel_index in enumerate(self.bus.sel):
|
||||
m.d.comb += [
|
||||
self.bus.dat_r[i*self.granularity:(i+1)*self.granularity]
|
||||
.eq(spi_io.r_data[i*self.granularity:(i+1)*self.granularity]),
|
||||
]
|
||||
|
||||
return m
|
|
@ -0,0 +1,68 @@
|
|||
from nmigen import *
|
||||
from nmigen_soc import csr
|
||||
from nmigen.utils import *
|
||||
|
||||
|
||||
__all__ = ["TimerCore"]
|
||||
|
||||
|
||||
class TimerCore(Elaboratable):
|
||||
def __init__(self, *, width, bus_data_width,
|
||||
clk_domain="sync", bus_granularity=None):
|
||||
self.width = width
|
||||
|
||||
bus_dw = bus_data_width
|
||||
if bus_granularity is None:
|
||||
bus_granularity = bus_data_width
|
||||
bus_gr = bus_granularity
|
||||
|
||||
with csr.Bank(name="timer",
|
||||
addr_width=log2_int(-(-(bus_dw+3*max(width, bus_dw))//bus_gr), need_pow2=False),
|
||||
data_width=bus_gr) as self.csr:
|
||||
self.csr.r += [
|
||||
csr.Register(
|
||||
"control", "rw", width=bus_dw,
|
||||
fields=[csr.Field("enable", startbit=0),
|
||||
csr.Field("update_enable", startbit=4)]
|
||||
),
|
||||
csr.Register(
|
||||
"load", "rw", width=max(width, bus_dw),
|
||||
fields=[csr.Field("value", width=self.width)]
|
||||
),
|
||||
csr.Register(
|
||||
"reload", "rw", width=max(width, bus_dw),
|
||||
fields=[csr.Field("value", width=self.width)]
|
||||
),
|
||||
csr.Register(
|
||||
"current", "r", width=max(width, bus_dw),
|
||||
fields=[csr.Field("value", width=self.width)]
|
||||
)
|
||||
]
|
||||
self.wb2csr = csr.WishboneCSRBridge(self.csr.dec.bus, data_width=bus_data_width)
|
||||
self.csr_bus = self.wb2csr.wb_bus
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
m.submodules += self.csr, self.wb2csr
|
||||
|
||||
# Value right now
|
||||
value = Signal(self.width)
|
||||
# When Timer is enabled, start counting
|
||||
with m.If(self.csr.r.control.f.enable.s):
|
||||
with m.If(value == 0):
|
||||
m.d.sync += value.eq(self.csr.r.reload.s)
|
||||
with m.Else():
|
||||
m.d.sync += value.eq(value - 1)
|
||||
# Otherwise, load Timer value to .value
|
||||
with m.Else():
|
||||
m.d.sync += value.eq(self.csr.r.load.s)
|
||||
|
||||
# If Timer allows updating value in memory, update
|
||||
m.d.comb += self.csr.r.current.set_val.eq(value)
|
||||
with m.If(self.csr.r.control.f.update_enable.s):
|
||||
m.d.sync += self.csr.r.current.set_stb.eq(1)
|
||||
with m.Else():
|
||||
m.d.sync += self.csr.r.current.set_stb.eq(0)
|
||||
|
||||
return m
|
|
@ -0,0 +1,90 @@
|
|||
from nmigen import *
|
||||
from nmigen_stdio import serial
|
||||
from nmigen_soc import csr
|
||||
from nmigen.utils import *
|
||||
|
||||
|
||||
__all__ = ["UARTCore"]
|
||||
|
||||
|
||||
class UARTCore(Elaboratable):
|
||||
def __init__(self, uart_pins, *, sys_clk_freq, bus_data_width,
|
||||
baudrate=115200, divisor_bits=32, bus_granularity=None):
|
||||
self.uart_pins = uart_pins
|
||||
if divisor_bits > 32:
|
||||
raise NotImplementedError("Currently only supports divisor values "
|
||||
"not wider than 32 bits")
|
||||
self.div_bits = divisor_bits
|
||||
self.div = int(sys_clk_freq/baudrate) - 1
|
||||
|
||||
bus_dw = bus_data_width
|
||||
if bus_granularity is None:
|
||||
bus_granularity = bus_data_width
|
||||
bus_gr = bus_granularity
|
||||
|
||||
with csr.Bank(name="uart", addr_width=log2_int(4*bus_dw//bus_gr),
|
||||
data_width=bus_gr) as self.csr:
|
||||
self.csr.r += [
|
||||
csr.Register(
|
||||
"control", "rw", width=bus_dw,
|
||||
fields=[csr.Field("divisor", "rw", width=self.div_bits, reset_value=self.div)]
|
||||
),
|
||||
csr.Register(
|
||||
"flags", "rw", width=bus_dw,
|
||||
fields=[csr.Field("rx_ready", "r", startbit=0, reset_value=0),
|
||||
csr.Field("tx_ready", "r", startbit=4, reset_value=1),
|
||||
csr.Field("rx_enable", "rw", startbit=8)]
|
||||
),
|
||||
csr.Register(
|
||||
"txdata", "w", width=bus_dw, fields=[csr.Field("value", width=8)]
|
||||
),
|
||||
csr.Register(
|
||||
"rxdata", "r", width=bus_dw, fields=[csr.Field("value", width=8)]
|
||||
)
|
||||
]
|
||||
self.wb2csr = csr.WishboneCSRBridge(self.csr.dec.bus, data_width=bus_data_width)
|
||||
self.csr_bus = self.wb2csr.wb_bus
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
m.submodules.rxtx = rxtx = serial.AsyncSerial(
|
||||
divisor=self.div, divisor_bits=self.div_bits, pins=self.uart_pins
|
||||
)
|
||||
rx = rxtx.rx
|
||||
tx = rxtx.tx
|
||||
|
||||
m.submodules += self.csr, self.wb2csr
|
||||
|
||||
# CSR - Divisor
|
||||
m.d.comb += rxtx.divisor.eq(self.csr.r.control.f.divisor.s)
|
||||
|
||||
# CSR - RX/TX
|
||||
tx_ack_now = Signal()
|
||||
m.d.sync += tx_ack_now.eq(self.csr.r.txdata.bus.w_stb)
|
||||
|
||||
# RX Ready Flag is read-once-and-clear
|
||||
with m.If(self.csr.r.flags.bus.r_stb):
|
||||
m.d.comb += [
|
||||
self.csr.r.flags.f.rx_ready.set_stb.eq(1),
|
||||
self.csr.r.flags.f.rx_ready.set_val.eq(0)
|
||||
]
|
||||
with m.Elif(rx.r_rdy):
|
||||
m.d.comb += [
|
||||
self.csr.r.flags.f.rx_ready.set_stb.eq(1),
|
||||
self.csr.r.flags.f.rx_ready.set_val.eq(1)
|
||||
]
|
||||
|
||||
m.d.comb += [
|
||||
# TX
|
||||
self.csr.r.flags.f.tx_ready.set_stb.eq(1),
|
||||
self.csr.r.flags.f.tx_ready.set_val.eq(~tx.busy),
|
||||
tx.ack.eq(tx_ack_now),
|
||||
tx.data.eq(self.csr.r.txdata.f.value.s),
|
||||
# RX
|
||||
self.csr.r.rxdata.f.value.set_stb.eq(1),
|
||||
self.csr.r.rxdata.f.value.set_val.eq(rx.data),
|
||||
rx.ack.eq(self.csr.r.flags.f.rx_enable.s)
|
||||
]
|
||||
|
||||
return m
|
|
@ -1,41 +0,0 @@
|
|||
import unittest
|
||||
|
||||
from nmigen import *
|
||||
from nmigen.back.pysim import *
|
||||
|
||||
from heavycomps import uart
|
||||
|
||||
|
||||
class Loopback(Elaboratable):
|
||||
def __init__(self, tuning_word=2**31):
|
||||
self.tx = uart.RS232TX(tuning_word)
|
||||
self.rx = uart.RS232RX(tuning_word)
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
m.submodules.tx = self.tx
|
||||
m.submodules.rx = self.rx
|
||||
m.d.comb += self.rx.rx.eq(self.tx.tx)
|
||||
return m
|
||||
|
||||
|
||||
class TestUART(unittest.TestCase):
|
||||
def test_loopback(self):
|
||||
dut = Loopback()
|
||||
test_vector = [32, 129, 201, 39, 0, 255]
|
||||
|
||||
with Simulator(Fragment.get(dut, None)) as sim:
|
||||
sim.add_clock(1e-6)
|
||||
|
||||
def send():
|
||||
for value in test_vector:
|
||||
yield from dut.tx.write(value)
|
||||
|
||||
def receive():
|
||||
for value in test_vector:
|
||||
received = yield from dut.rx.read()
|
||||
self.assertEqual(received, value)
|
||||
|
||||
sim.add_sync_process(send)
|
||||
sim.add_sync_process(receive)
|
||||
sim.run()
|
|
@ -1,123 +0,0 @@
|
|||
from nmigen import *
|
||||
from nmigen.lib.cdc import MultiReg
|
||||
|
||||
|
||||
__all__ = ["RS232RX", "RS232TX"]
|
||||
|
||||
|
||||
class RS232RX(Elaboratable):
|
||||
def __init__(self, tuning_word):
|
||||
self.rx = Signal()
|
||||
self.data = Signal(8)
|
||||
self.stb = Signal()
|
||||
self.tuning_word = tuning_word
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
uart_clk_rxen = Signal()
|
||||
phase_accumulator_rx = Signal(32)
|
||||
|
||||
rx = Signal()
|
||||
m.submodules += MultiReg(self.rx, rx)
|
||||
rx_r = Signal()
|
||||
rx_reg = Signal(8)
|
||||
rx_bitcount = Signal(4)
|
||||
rx_busy = Signal()
|
||||
rx_done = self.stb
|
||||
rx_data = self.data
|
||||
m.d.sync += [
|
||||
rx_done.eq(0),
|
||||
rx_r.eq(rx)
|
||||
]
|
||||
with m.If(~rx_busy):
|
||||
with m.If(~rx & rx_r): # look for start bit
|
||||
m.d.sync += [
|
||||
rx_busy.eq(1),
|
||||
rx_bitcount.eq(0)
|
||||
]
|
||||
with m.Else():
|
||||
with m.If(uart_clk_rxen):
|
||||
m.d.sync += rx_bitcount.eq(rx_bitcount + 1)
|
||||
with m.If(rx_bitcount == 0):
|
||||
with m.If(rx): # verify start bit
|
||||
m.d.sync += rx_busy.eq(0)
|
||||
with m.Elif(rx_bitcount == 9):
|
||||
m.d.sync += rx_busy.eq(0)
|
||||
with m.If(rx): # verify stop bit
|
||||
m.d.sync += [
|
||||
rx_data.eq(rx_reg),
|
||||
rx_done.eq(1)
|
||||
]
|
||||
with m.Else():
|
||||
m.d.sync += rx_reg.eq(Cat(rx_reg[1:], rx))
|
||||
with m.If(rx_busy):
|
||||
m.d.sync += Cat(phase_accumulator_rx, uart_clk_rxen).eq(phase_accumulator_rx + self.tuning_word)
|
||||
with m.Else():
|
||||
m.d.sync += Cat(phase_accumulator_rx, uart_clk_rxen).eq(2**31)
|
||||
|
||||
return m
|
||||
|
||||
def read(self):
|
||||
while not (yield self.stb):
|
||||
yield
|
||||
value = yield self.data
|
||||
# clear stb, otherwise multiple calls to this generator keep returning the same value
|
||||
yield
|
||||
return value
|
||||
|
||||
|
||||
class RS232TX(Elaboratable):
|
||||
def __init__(self, tuning_word):
|
||||
self.tx = Signal(reset=1)
|
||||
self.data = Signal(8)
|
||||
self.stb = Signal()
|
||||
self.ack = Signal()
|
||||
self.tuning_word = tuning_word
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
uart_clk_txen = Signal()
|
||||
phase_accumulator_tx = Signal(32)
|
||||
|
||||
tx_reg = Signal(8)
|
||||
tx_bitcount = Signal(4)
|
||||
tx_busy = Signal()
|
||||
m.d.sync += self.ack.eq(0)
|
||||
with m.If(self.stb & ~tx_busy & ~self.ack):
|
||||
m.d.sync += [
|
||||
tx_reg.eq(self.data),
|
||||
tx_bitcount.eq(0),
|
||||
tx_busy.eq(1),
|
||||
self.tx.eq(0)
|
||||
]
|
||||
with m.Elif(uart_clk_txen & tx_busy):
|
||||
m.d.sync += tx_bitcount.eq(tx_bitcount + 1)
|
||||
with m.If(tx_bitcount == 8):
|
||||
m.d.sync += self.tx.eq(1)
|
||||
with m.Elif(tx_bitcount == 9):
|
||||
m.d.sync += [
|
||||
self.tx.eq(1),
|
||||
tx_busy.eq(0),
|
||||
self.ack.eq(1)
|
||||
]
|
||||
with m.Else():
|
||||
m.d.sync += [
|
||||
self.tx.eq(tx_reg[0]),
|
||||
tx_reg.eq(Cat(tx_reg[1:], 0))
|
||||
]
|
||||
with m.If(tx_busy):
|
||||
m.d.sync += Cat(phase_accumulator_tx, uart_clk_txen).eq(phase_accumulator_tx + self.tuning_word)
|
||||
with m.Else():
|
||||
m.d.sync += Cat(phase_accumulator_tx, uart_clk_txen).eq(0)
|
||||
|
||||
return m
|
||||
|
||||
def write(self, data):
|
||||
yield self.stb.eq(1)
|
||||
yield self.data.eq(data)
|
||||
yield
|
||||
while not (yield self.ack):
|
||||
yield
|
||||
yield self.stb.eq(0)
|
|
@ -1,210 +0,0 @@
|
|||
from enum import Enum
|
||||
from functools import reduce
|
||||
from operator import or_
|
||||
|
||||
from nmigen import *
|
||||
from nmigen.hdl.rec import *
|
||||
|
||||
|
||||
__all__ = ["Cycle", "Interface", "Arbiter", "Decoder", "InterconnectShared"]
|
||||
|
||||
|
||||
class Cycle(Enum):
|
||||
CLASSIC = 0
|
||||
CONSTANT = 1
|
||||
INCREMENT = 2
|
||||
END = 7
|
||||
|
||||
|
||||
wishbone_layout = [
|
||||
("adr", 30, DIR_FANOUT),
|
||||
("dat_w", 32, DIR_FANOUT),
|
||||
("dat_r", 32, DIR_FANIN),
|
||||
("sel", 4, DIR_FANOUT),
|
||||
("cyc", 1, DIR_FANOUT),
|
||||
("stb", 1, DIR_FANOUT),
|
||||
("ack", 1, DIR_FANIN),
|
||||
("we", 1, DIR_FANOUT),
|
||||
("cti", 3, DIR_FANOUT),
|
||||
("bte", 2, DIR_FANOUT),
|
||||
("err", 1, DIR_FANIN)
|
||||
]
|
||||
|
||||
|
||||
class Interface(Record):
|
||||
def __init__(self):
|
||||
Record.__init__(self, wishbone_layout)
|
||||
|
||||
def _do_transaction(self):
|
||||
yield self.cyc.eq(1)
|
||||
yield self.stb.eq(1)
|
||||
yield
|
||||
while not (yield self.ack):
|
||||
yield
|
||||
yield self.cyc.eq(0)
|
||||
yield self.stb.eq(0)
|
||||
|
||||
def write(self, adr, dat, sel=None):
|
||||
if sel is None:
|
||||
sel = 2**len(self.sel) - 1
|
||||
yield self.adr.eq(adr)
|
||||
yield self.dat_w.eq(dat)
|
||||
yield self.sel.eq(sel)
|
||||
yield self.we.eq(1)
|
||||
yield from self._do_transaction()
|
||||
|
||||
def read(self, adr):
|
||||
yield self.adr.eq(adr)
|
||||
yield self.we.eq(0)
|
||||
yield from self._do_transaction()
|
||||
return (yield self.dat_r)
|
||||
|
||||
|
||||
class SRAM(Elaboratable):
|
||||
def __init__(self, mem, read_only=False, bus=None):
|
||||
self.mem = mem
|
||||
self.read_only = read_only
|
||||
if bus is None:
|
||||
bus = Interface()
|
||||
self.bus = bus
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
if self.mem.width > len(self.bus.dat_r):
|
||||
raise NotImplementedError
|
||||
|
||||
# read
|
||||
m.submodules.rdport = rdport = self.mem.read_port()
|
||||
m.d.comb += [
|
||||
rdport.addr.eq(self.bus.adr[:len(rdport.addr)]),
|
||||
self.bus.dat_r.eq(rdport.data)
|
||||
]
|
||||
|
||||
# write
|
||||
if not self.read_only:
|
||||
m.submodules.wrport = wrport = self.mem.write_port(granularity=8)
|
||||
m.d.comb += [
|
||||
wrport.addr.eq(self.bus.adr[:len(rdport.addr)]),
|
||||
wrport.data.eq(self.bus.dat_w)
|
||||
]
|
||||
for i in range(4):
|
||||
m.d.comb += wrport.en[i].eq(self.bus.cyc & self.bus.stb & self.bus.we & self.bus.sel[i])
|
||||
|
||||
# generate ack
|
||||
m.d.sync += self.bus.ack.eq(0)
|
||||
with m.If(self.bus.cyc & self.bus.stb & ~self.bus.ack):
|
||||
m.d.sync += self.bus.ack.eq(1)
|
||||
|
||||
return m
|
||||
|
||||
|
||||
class RoundRobin(Elaboratable):
|
||||
def __init__(self, n):
|
||||
self.n = n
|
||||
self.request = Signal(n)
|
||||
self.grant = Signal(max=n)
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
with m.Switch(self.grant):
|
||||
for i in range(self.n):
|
||||
with m.Case(i):
|
||||
with m.If(~self.request[i]):
|
||||
for j in reversed(range(i+1, i+self.n)):
|
||||
t = j % self.n
|
||||
with m.If(self.request[t]):
|
||||
m.d.sync += self.grant.eq(t)
|
||||
return m
|
||||
|
||||
|
||||
class Arbiter(Elaboratable):
|
||||
def __init__(self, masters, target):
|
||||
self.masters = masters
|
||||
self.target = target
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
m.submodules.rr = rr = RoundRobin(len(self.masters))
|
||||
|
||||
# mux master->target signals
|
||||
for name, size, direction in wishbone_layout:
|
||||
if direction == DIR_FANOUT:
|
||||
choices = Array(getattr(m, name) for m in self.masters)
|
||||
m.d.comb += getattr(self.target, name).eq(choices[rr.grant])
|
||||
|
||||
# connect target->master signals
|
||||
for name, size, direction in wishbone_layout:
|
||||
if direction == DIR_FANIN:
|
||||
source = getattr(self.target, name)
|
||||
for i, master in enumerate(self.masters):
|
||||
dest = getattr(master, name)
|
||||
if name == "ack" or name == "err":
|
||||
m.d.comb += dest.eq(source & (rr.grant == i))
|
||||
else:
|
||||
m.d.comb += dest.eq(source)
|
||||
|
||||
# connect bus requests to round-robin selector
|
||||
reqs = [m.cyc & ~m.ack for m in self.masters]
|
||||
m.d.comb += rr.request.eq(Cat(*reqs))
|
||||
|
||||
return m
|
||||
|
||||
|
||||
class Decoder(Elaboratable):
|
||||
def __init__(self, master, targets, register=False):
|
||||
self.master = master
|
||||
self.targets = targets
|
||||
self.register = register
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
|
||||
nt = len(self.targets)
|
||||
target_sel = Signal(nt)
|
||||
target_sel_r = Signal(nt)
|
||||
|
||||
# decode target addresses
|
||||
for i, (fun, bus) in enumerate(self.targets):
|
||||
m.d.comb += target_sel[i].eq(fun(self.master.adr))
|
||||
if self.register:
|
||||
m.d.sync += target_sel_r.eq(target_sel)
|
||||
else:
|
||||
m.d.comb += target_sel_r.eq(target_sel)
|
||||
|
||||
# connect master->targets signals except cyc
|
||||
for target in self.targets:
|
||||
for name, size, direction in wishbone_layout:
|
||||
if direction == DIR_FANOUT and name != "cyc":
|
||||
m.d.comb += getattr(target[1], name).eq(getattr(self.master, name))
|
||||
|
||||
# combine cyc with target selection signals
|
||||
for i, target in enumerate(self.targets):
|
||||
m.d.comb += target[1].cyc.eq(self.master.cyc & target_sel[i])
|
||||
|
||||
# generate master ack (resp. err) by ORing all target acks (resp. errs)
|
||||
m.d.comb += [
|
||||
self.master.ack.eq(reduce(or_, [target[1].ack for target in self.targets])),
|
||||
self.master.err.eq(reduce(or_, [target[1].err for target in self.targets]))
|
||||
]
|
||||
|
||||
# mux (1-hot) target data return
|
||||
masked = [Repl(target_sel_r[i], len(self.master.dat_r)) & self.targets[i][1].dat_r for i in range(nt)]
|
||||
m.d.comb += self.master.dat_r.eq(reduce(or_, masked))
|
||||
|
||||
return m
|
||||
|
||||
|
||||
class InterconnectShared(Module):
|
||||
def __init__(self, masters, targets, register=False):
|
||||
self.masters = masters
|
||||
self.targets = targets
|
||||
self.register = register
|
||||
|
||||
def elaborate(self, platform):
|
||||
m = Module()
|
||||
shared = Interface()
|
||||
m.submodules.arbiter = Arbiter(self.masters, shared)
|
||||
m.submodules.decoder = Decoder(shared, self.targets, self.register)
|
||||
return m
|
|
@ -2,6 +2,6 @@ from setuptools import setup, find_packages
|
|||
|
||||
setup(
|
||||
name="heavycomps",
|
||||
install_requires=["nmigen"],
|
||||
install_requires=["nmigen", "nmigen_stdio", "nmigen_soc"],
|
||||
packages=find_packages()
|
||||
)
|
||||
|
|
|
@ -8,6 +8,8 @@ let
|
|||
helloworld_ecp5 = import ./examples/helloworld_ecp5.nix { inherit pkgs hx; };
|
||||
helloworld_kintex7 = import ./examples/helloworld_kintex7.nix { inherit pkgs hx; };
|
||||
simplesoc_ecp5 = import ./examples/simplesoc_ecp5.nix { inherit pkgs hx; };
|
||||
|
||||
testing_ecp5 = import ./examples/testing_ecp5.nix { inherit pkgs hx; };
|
||||
};
|
||||
in
|
||||
builtins.mapAttrs (name: value: pkgs.lib.hydraJob value) jobs
|
||||
|
|
Loading…
Reference in New Issue