Compare commits
6 Commits
f846cc1fab
...
ac746a8c5a
Author | SHA1 | Date | |
---|---|---|---|
ac746a8c5a | |||
bf482e740d | |||
a05af8739c | |||
348f6d5ba6 | |||
96dabe013a | |||
32fb8383d1 |
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -1,6 +1,3 @@
|
||||
[submodule "gateware/amaranth-boards"]
|
||||
path = gateware/amaranth-boards
|
||||
url = https://github.com/amaranth-lang/amaranth-boards
|
||||
[submodule "gateware/jtagtap"]
|
||||
path = gateware/jtagtap
|
||||
url = git@github.com:davidlenfesty/jtagtap
|
||||
|
@ -9,3 +9,6 @@ edition = "2021"
|
||||
riscv-rt = "0.11.0"
|
||||
panic-halt = "0.2.0"
|
||||
embedded-hal = "0.2.7"
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
|
@ -1,7 +1,7 @@
|
||||
MEMORY
|
||||
{
|
||||
RAM : ORIGIN = 0x01001000, LENGTH = 4K
|
||||
FLASH : ORIGIN = 0x01000000, LENGTH = 4K
|
||||
RAM : ORIGIN = 0x01002000, LENGTH = 4K
|
||||
FLASH : ORIGIN = 0x01000000, LENGTH = 8K
|
||||
}
|
||||
|
||||
REGION_ALIAS("REGION_TEXT", FLASH);
|
||||
|
@ -1,6 +1,6 @@
|
||||
//! Quick and hacky ethernet thing to test
|
||||
|
||||
const LITEETH_BASE: u32 = 0x0200_0000;
|
||||
const LITEETH_BASE: u32 = 0x0300_0000;
|
||||
|
||||
const ETHMAC_SRAM_WRITER_EV_PENDING: u32 = LITEETH_BASE + 0x810;
|
||||
const ETHMAC_SRAM_WRITER_EV_ENABLE: u32 = LITEETH_BASE + 0x814;
|
||||
|
104
firmware/src/i2c.rs
Normal file
104
firmware/src/i2c.rs
Normal file
@ -0,0 +1,104 @@
|
||||
//! I2C driver for my amlib i2c interface implementation.
|
||||
//!
|
||||
//! See `gateware/i2c.py` for register information
|
||||
|
||||
|
||||
/// TODO repr(C) a register bank, and add instances
|
||||
|
||||
// Using the blocking API because the peripheral requires fairly tight timing to operate
|
||||
// correctly, and I don't feel like writing the gateware to resolve that.
|
||||
use embedded_hal::blocking::i2c::{Write, Read, SevenBitAddress, Transactional, Operation};
|
||||
use crate::{read_reg, write_reg};
|
||||
use core::arch::asm;
|
||||
|
||||
// TODO I think there may be bus address semantics I'm not 100% on.
|
||||
// There's a possiblity these addresses are wrong, and they need to be 4 bytes each
|
||||
const CR: u32 = 0;
|
||||
const SR: u32 = 1;
|
||||
const DWR: u32 = 2;
|
||||
const DRR: u32 = 3;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Error {
|
||||
/// Device is busy for some reason
|
||||
Busy,
|
||||
/// I2C bus returned a NACK
|
||||
Nack,
|
||||
}
|
||||
|
||||
pub struct AmlibI2c {
|
||||
base_addr: u32,
|
||||
}
|
||||
|
||||
impl AmlibI2c {
|
||||
pub fn new(base_addr: u32) -> Self {
|
||||
AmlibI2c { base_addr }
|
||||
}
|
||||
|
||||
fn wait_while_busy(&self) {
|
||||
unsafe {
|
||||
while read_reg::<u32>(self.base_addr + SR) & 1 != 0 {
|
||||
asm!("nop");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write<SevenBitAddress> for AmlibI2c {
|
||||
// TODO errors
|
||||
type Error = Error;
|
||||
|
||||
fn write(&mut self, address: SevenBitAddress, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||
unsafe {
|
||||
if (read_reg::<u32>(self.base_addr + SR) & 1) != 0 {
|
||||
return Err(Error::Busy);
|
||||
}
|
||||
|
||||
// START
|
||||
write_reg(self.base_addr + CR, 0x01u32);
|
||||
// Pre-load data w/ address (R/~W = 0)
|
||||
write_reg(self.base_addr + DWR, (address << 1) as u32);
|
||||
|
||||
self.wait_while_busy();
|
||||
|
||||
// Send address byte
|
||||
write_reg(self.base_addr + CR, 0x04u32);
|
||||
if read_reg::<u32>(self.base_addr + SR) & 0x02 != 0 {
|
||||
return Err(Error::Nack);
|
||||
}
|
||||
|
||||
for byte in bytes {
|
||||
// Write byte
|
||||
write_reg(self.base_addr + DWR, *byte as u32);
|
||||
self.wait_while_busy();
|
||||
// Send byte once done sending the last byte
|
||||
write_reg(self.base_addr + CR, 0x04u32);
|
||||
}
|
||||
|
||||
self.wait_while_busy();
|
||||
// STOP
|
||||
write_reg(self.base_addr + CR, 0x02u32);
|
||||
|
||||
self.wait_while_busy();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Read<SevenBitAddress> for AmlibI2c {
|
||||
// TODO errors
|
||||
type Error = Error;
|
||||
|
||||
fn read(&mut self, address: SevenBitAddress, buffer: &mut [u8]) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Transactional for AmlibI2c {
|
||||
type Error = Error;
|
||||
|
||||
fn exec<'a>(&mut self, address: u8, operations: &mut [Operation<'a>])
|
||||
-> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -4,12 +4,14 @@
|
||||
extern crate panic_halt;
|
||||
|
||||
use core::{arch::asm, ptr::{write, read}};
|
||||
use core::fmt::Write;
|
||||
|
||||
use embedded_hal::prelude::_embedded_hal_blocking_i2c_Write;
|
||||
use riscv_rt::entry;
|
||||
|
||||
mod eth;
|
||||
mod i2c;
|
||||
mod uart;
|
||||
|
||||
// use `main` as the entry point of this application
|
||||
// `main` is not allowed to return
|
||||
@ -26,12 +28,15 @@ fn main() -> ! {
|
||||
//};
|
||||
let blink_period = 10_000_000u32;
|
||||
|
||||
let mut i2c = i2c::AmlibI2c::new(0x01003000);
|
||||
let data = [0u8, 2u8];
|
||||
i2c.write(0xAA, &data).unwrap();
|
||||
//let mut i2c = i2c::AmlibI2c::new(0x0200_0000);
|
||||
//let data = [0u8, 2u8];
|
||||
//i2c.write(0xAA, &data).unwrap();
|
||||
|
||||
let mut uart = uart::AmlibUart::new(0x0200_0040);
|
||||
|
||||
loop {
|
||||
//eth::tranmsit();
|
||||
uart.write_str("Hello world!\r\n");
|
||||
write_led(0);
|
||||
busy_wait(blink_period);
|
||||
write_led(1);
|
||||
@ -48,7 +53,7 @@ fn busy_wait(num_nops: u32) {
|
||||
}
|
||||
|
||||
fn write_led(val: u32) {
|
||||
unsafe { write_reg(0x0100200, val); }
|
||||
unsafe { write_reg(0x01003000, val); }
|
||||
}
|
||||
|
||||
unsafe fn write_reg<T>(addr: u32, value: T) {
|
||||
|
75
firmware/src/uart.rs
Normal file
75
firmware/src/uart.rs
Normal file
@ -0,0 +1,75 @@
|
||||
//! Quick and dirty uart driver
|
||||
|
||||
/// TODO repr(C) a register bank, and add instances
|
||||
///
|
||||
use core::ptr::{read_volatile, write_volatile};
|
||||
use core::fmt::{Write};
|
||||
|
||||
// TODO these offsets may be wrong. I'm still unsure about CSR semantics
|
||||
const REG_DIVISOR_OFFSET: u32 = 0;
|
||||
const REG_SR_OFFSET: u32 = 2;
|
||||
const REG_DR_OFFSET: u32 = 3;
|
||||
|
||||
pub const FLAG_SR_TX_FULL: u8 = (1 << 0);
|
||||
pub const FLAG_SR_TX_EMPTY: u8 = (1 << 1);
|
||||
pub const FLAG_SR_RX_FULL: u8 = (1 << 2);
|
||||
pub const FLAG_SR_RX_EMPTY: u8 = (1 << 3);
|
||||
|
||||
pub enum Error {
|
||||
TxFull,
|
||||
RxEmpty,
|
||||
}
|
||||
|
||||
pub struct AmlibUart {
|
||||
base_addr: u32,
|
||||
}
|
||||
|
||||
impl AmlibUart {
|
||||
pub fn new(base_addr: u32) -> Self {
|
||||
Self {
|
||||
base_addr,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn read_status(&mut self) -> u8 {
|
||||
unsafe {
|
||||
read_volatile((self.base_addr + REG_SR_OFFSET) as *const u8)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_get_char(&mut self) -> Result<u8, Error> {
|
||||
if self.read_status() & FLAG_SR_RX_EMPTY != 0 {
|
||||
return Err(Error::RxEmpty);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
Ok(read_volatile((self.base_addr + REG_DR_OFFSET) as *const u8))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn try_put_char(&mut self, c: u8) -> Result<(), Error> {
|
||||
if self.read_status() & FLAG_SR_TX_FULL != 0 {
|
||||
return Err(Error::TxFull);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
write_volatile((self.base_addr + REG_DR_OFFSET) as *mut u8, c);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Blocking implementation of write
|
||||
impl Write for AmlibUart {
|
||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
||||
for b in s.as_bytes() {
|
||||
// It's okay to loop on this because we'll always clear the buffer
|
||||
while let Err(Error::TxFull) = self.try_put_char(*b) {}
|
||||
//self.try_put_char(*b);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
Subproject commit 1d82f2ece15ddcce964b9d3be1d13e8a343537eb
|
@ -41,7 +41,7 @@ class I2C(Elaboratable):
|
||||
# [3]: read - read a byte from the bus
|
||||
# [4]: read_ack - ACK value that gets written out during a read operation
|
||||
# [5]: read_ack_en - Hacky solution to determine if we want to save read_ack
|
||||
self.CR = Element(6, Element.Access.W, name="CR")
|
||||
self.CR = Element(6, Element.Access.W, name="I2C_CR")
|
||||
|
||||
# Status register
|
||||
#
|
||||
@ -49,27 +49,28 @@ class I2C(Elaboratable):
|
||||
# [0]: busy - bus is busy operating
|
||||
# [1]: ack - an ACK has been received from a bus slave
|
||||
# [2]: read_ack - a convenience read field to see value of CR->read_ack
|
||||
self.SR = Element(3, Element.Access.R, name="SR")
|
||||
self.SR = Element(3, Element.Access.R, name="I2C_SR")
|
||||
|
||||
# Data write register
|
||||
#
|
||||
# Latches in data to be written when write signal is applied.
|
||||
self.DWR = Element(8, Element.Access.W, name="DWR")
|
||||
self.DWR = Element(8, Element.Access.W, name="I2C_DWR")
|
||||
|
||||
# Data read register
|
||||
#
|
||||
# Only presents valid data after 'read' has started, and once 'busy' is no longer asserted.
|
||||
self.DRR = Element(8, Element.Access.R, name="DRR")
|
||||
self.DRR = Element(8, Element.Access.R, name="I2C_DRR")
|
||||
|
||||
# Set up CSR bus
|
||||
addr_width = ceil(log2(64)) # Support up to 64 registers just because
|
||||
data_width = 8 # 32 bit bus
|
||||
self._csr_mux = Multiplexer(addr_width=addr_width, data_width=data_width)
|
||||
# TODO export the addresses of these somehow
|
||||
self._csr_mux.add(self.CR)
|
||||
self._csr_mux.add(self.SR)
|
||||
self._csr_mux.add(self.DWR)
|
||||
self._csr_mux.add(self.DRR)
|
||||
# TODO export these addresses into some config file
|
||||
cr_start, _stop = self._csr_mux.add(self.CR)
|
||||
sr_start, _stop = self._csr_mux.add(self.SR)
|
||||
dwr_start, _stop = self._csr_mux.add(self.DWR)
|
||||
drr_start, _stop = self._csr_mux.add(self.DRR)
|
||||
print(f"I2C added. CR 0x{cr_start:x}, SR 0x{sr_start:x}, DWR 0x{dwr_start:x}, DRR 0x{drr_start:x}")
|
||||
self.bus = self._csr_mux.bus
|
||||
|
||||
# Set up I2C initiator submodule
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
from amaranth import *
|
||||
from amaranth.sim import *
|
||||
from amaranth_boards import colorlight_i9
|
||||
from platforms import *
|
||||
from amaranth_soc.wishbone import *
|
||||
from amaranth_soc import csr
|
||||
from amaranth_soc.csr.wishbone import WishboneCSRBridge
|
||||
@ -122,7 +122,7 @@ class Core(Elaboratable):
|
||||
fw = load_firmware_for_mem()
|
||||
|
||||
# Hook up memory space
|
||||
self.rom = ROM(fw)
|
||||
self.rom = ROM(size_bytes=8192, data=fw)
|
||||
m.submodules.rom = self.rom
|
||||
# Problem: not sure to handle how we do byte vs word addressing properly
|
||||
# So doing this shift is a bit of a hacky way to impl anything
|
||||
@ -143,10 +143,8 @@ class Core(Elaboratable):
|
||||
# Create CSR bus and connect it to Wishbone
|
||||
self.csr = csr.Decoder(addr_width=10, data_width=8)
|
||||
m.submodules.csr = self.csr
|
||||
print(f"CSR bus added at 0x{start:08x}")
|
||||
|
||||
# I2C (connected to DAC for VCO and ADC?)
|
||||
Signal()
|
||||
if platform is not None:
|
||||
i2c_pads = platform.request("i2c")
|
||||
else:
|
||||
@ -158,17 +156,29 @@ class Core(Elaboratable):
|
||||
m.d.comb += i2c_pads.scl.i.eq(1)
|
||||
self.i2c = i2c.I2C(50e6, 100e3, i2c_pads)
|
||||
m.submodules.i2c = self.i2c
|
||||
self.csr.add(self.i2c.bus)
|
||||
i2c_start, _stop, _step = self.csr.add(self.i2c.bus)
|
||||
print(f"LED added to CSR at 0x{i2c_start}")
|
||||
|
||||
if platform is not None:
|
||||
uart_pads = platform.request("uart", 1)
|
||||
else:
|
||||
uart_pads = None
|
||||
# TODO spread sysclk freq through design
|
||||
self.uart = uart.UART(50e6, 115_200, pins=uart_pads)
|
||||
m.submodules.uart = self.uart
|
||||
uart_start, _stop, _step = self.csr.add(self.uart.bus)
|
||||
print(f"UART added to CSR at 0x{uart_start:x}")
|
||||
|
||||
self.csr_bridge = WishboneCSRBridge(self.csr.bus, data_width=32, name="CSR")
|
||||
m.submodules.csr_bridge = self.csr_bridge
|
||||
# TODO shouldn't have to hard-specify this address
|
||||
start, _stop, _step = self.decoder.add(self.csr_bridge.wb_bus, addr=0x01003000)
|
||||
start, _stop, _step = self.decoder.add(self.csr_bridge.wb_bus, addr=0x02000000)
|
||||
print(f"CSR bus added at 0x{start:08x}")
|
||||
|
||||
# Ethernet
|
||||
self.eth = LiteEth(self.eth_interface)
|
||||
m.submodules.eth = self.eth
|
||||
#start, _stop, _step = self.decoder.add(self.eth, addr=0x02000000)
|
||||
start, _stop, _step = self.decoder.add(self.eth, addr=0x03000000)
|
||||
print(f"LiteETH added at 0x{start:08x}")
|
||||
|
||||
# Connect arbiter to decoder
|
||||
@ -187,6 +197,7 @@ class SoC(Elaboratable):
|
||||
pass
|
||||
|
||||
def elaborate(self, platform):
|
||||
# TODO pull I2C and UART into here instead of the "core"
|
||||
if platform is not None:
|
||||
clk25 = platform.request("clk25")
|
||||
led_signal = platform.request("led")
|
||||
@ -229,7 +240,7 @@ if __name__ == "__main__":
|
||||
if args.build:
|
||||
# Overrides are available via AMARANTH_<override_variable_name> env variable, or kwarg
|
||||
# TODO fix platform so I don't have to manually specify MDIO signal
|
||||
colorlight_i9.Colorlight_i9_Platform().build(SoC(), debug_verilog=args.gen_debug_verilog, nextpnr_opts="--router router1", add_preferences="LOCATE COMP \"top.eth.core.rgmii_eth_mdio\" SITE \"P5\";\n")
|
||||
Colorlight_i9_Platform().build(SoC(), debug_verilog=args.gen_debug_verilog, nextpnr_opts="--router router1", add_preferences="LOCATE COMP \"top.eth.core.rgmii_eth_mdio\" SITE \"P5\";\n")
|
||||
|
||||
if args.test:
|
||||
if args.save_vcd:
|
||||
|
@ -2,25 +2,28 @@ from amaranth import *
|
||||
from amaranth_soc.wishbone import *
|
||||
from amaranth_soc.memory import *
|
||||
|
||||
from math import log2, ceil
|
||||
|
||||
# TODO impl select
|
||||
|
||||
|
||||
# We sub-class wishbone.Interface here because it needs to be a bus object to be added as a window to Wishbone stuff
|
||||
class ROM(Elaboratable, Interface):
|
||||
def __init__(self, data=None):
|
||||
def __init__(self, *, size_bytes=4096, data=None):
|
||||
#self.size = len(data)
|
||||
self.data = Memory(width=32, depth=(4096 >> 2), init=data)
|
||||
addr_width = ceil(log2(size_bytes >> 2))
|
||||
self.data = Memory(width=32, depth=size_bytes >> 2, init=data)
|
||||
self.r = self.data.read_port()
|
||||
|
||||
# Need to init Interface
|
||||
Interface.__init__(self, addr_width=10, data_width=32, granularity=8)
|
||||
Interface.__init__(self, addr_width=addr_width, data_width=32, granularity=8)
|
||||
|
||||
# This is effectively a "window", and it has a certain set of resources
|
||||
# 12 = log2(4096)
|
||||
memory_map = MemoryMap(addr_width=12, data_width=8)
|
||||
memory_map = MemoryMap(addr_width=addr_width + 2, data_width=8)
|
||||
# TODO need to unify how I deal with size
|
||||
# In this case, one resource, which is out memory
|
||||
memory_map.add_resource(self.data, name="rom_data", size=(4096 >> 2))
|
||||
memory_map.add_resource(self.data, name="rom_data", size=size_bytes)
|
||||
|
||||
self.memory_map = memory_map
|
||||
|
||||
@ -52,21 +55,21 @@ class ROM(Elaboratable, Interface):
|
||||
# TODO support read segmentation or whatever it's called, where you read/write certian bytes from memory
|
||||
# Otherwise we can't store individual bytes, and this will wreck shit in weird ways.
|
||||
class RAM(Elaboratable, Interface):
|
||||
def __init__(self):
|
||||
#self.size = len(data)
|
||||
self.data = Memory(width=32, depth=(4096 >> 2))
|
||||
def __init__(self, *, size_bytes=4096):
|
||||
addr_width = ceil(log2(size_bytes >> 2))
|
||||
self.data = Memory(width=32, depth=size_bytes >> 2)
|
||||
self.r = self.data.read_port()
|
||||
self.w = self.data.write_port()
|
||||
|
||||
# Need to init Interface
|
||||
Interface.__init__(self, addr_width=10, data_width=32, granularity=8)
|
||||
Interface.__init__(self, addr_width=addr_width, data_width=32, granularity=8)
|
||||
|
||||
# This is effectively a "window", and it has a certain set of resources
|
||||
# 12 = log2(4096)
|
||||
memory_map = MemoryMap(addr_width=12, data_width=8)
|
||||
memory_map = MemoryMap(addr_width=addr_width + 2, data_width=8)
|
||||
# TODO need to unify how I deal with size
|
||||
# In this case, one resource, which is out memory
|
||||
memory_map.add_resource(self.data, name="ram_data", size=(4096 >> 2))
|
||||
memory_map.add_resource(self.data, name="ram_data", size=size_bytes)
|
||||
|
||||
self.memory_map = memory_map
|
||||
|
||||
|
1
gateware/platforms/__init__.py
Normal file
1
gateware/platforms/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
from .colorlight_i9 import *
|
136
gateware/platforms/colorlight_i9.py
Normal file
136
gateware/platforms/colorlight_i9.py
Normal file
@ -0,0 +1,136 @@
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
from amaranth.build import *
|
||||
from amaranth.vendor.lattice_ecp5 import *
|
||||
from amaranth_boards.resources import *
|
||||
|
||||
|
||||
__all__ = ["Colorlight_i9_Platform"]
|
||||
|
||||
|
||||
class Colorlight_i9_Platform(LatticeECP5Platform):
|
||||
device = "LFE5U-45F"
|
||||
package = "BG381"
|
||||
speed = "6"
|
||||
default_clk = "clk25"
|
||||
|
||||
resources = [
|
||||
Resource("clk25", 0, Pins("P3", dir="i"), Clock(25e6), Attrs(IO_TYPE="LVCMOS33")),
|
||||
|
||||
*LEDResources(pins="L2", invert = True,
|
||||
attrs=Attrs(IO_TYPE="LVCMOS33", DRIVE="4")),
|
||||
|
||||
#*ButtonResources(pins="M13", invert = True,
|
||||
# attrs=Attrs(IO_TYPE="LVCMOS33", PULLMODE="UP")),
|
||||
|
||||
UARTResource(0,
|
||||
tx="E17",
|
||||
rx="D18",
|
||||
attrs=Attrs(IO_TYPE="LVCMOS33")
|
||||
),
|
||||
UARTResource(1,
|
||||
tx="P16",
|
||||
rx="L5",
|
||||
attrs=Attrs(IO_TYPE="LVCMOS33")
|
||||
),
|
||||
UARTResource(2,
|
||||
tx="J18",
|
||||
rx="J16",
|
||||
attrs=Attrs(IO_TYPE="LVCMOS33")
|
||||
),
|
||||
|
||||
# SPIFlash (W25Q32JV) 1x/2x/4x speed
|
||||
Resource("spi_flash", 0,
|
||||
Subsignal("cs", PinsN("R2", dir="o")),
|
||||
# Subsignal("clk", Pins("", dir="i")), # driven through USRMCLK
|
||||
Subsignal("cipo", Pins("V2", dir="i")), # Chip: DI/IO0
|
||||
Subsignal("copi", Pins("W2", dir="o")), # DO/IO1
|
||||
Attrs(IO_TYPE="LVCMOS33")
|
||||
),
|
||||
|
||||
# 2x ESMT M12L16161A-5T 1M x 16bit 200MHz SDRAMs (organized as 1M x 32bit)
|
||||
# 2x WinBond W9816G6JH-6 1M x 16bit 166MHz SDRAMs (organized as 1M x 32bit) are lso reported
|
||||
SDRAMResource(0,
|
||||
clk="B9", we_n="A10", cas_n="A9", ras_n="B10",
|
||||
ba="B11 C8", a="B13 C14 A16 A17 B16 B15 A14 A13 A12 A11 B12",
|
||||
dq="D15 E14 E13 D12 E12 D11 C10 B17 B8 A8 C7 A7 A6 B6 A5 B5 "
|
||||
"D5 C5 D6 C6 E7 D7 E8 D8 E9 D9 E11 C11 C12 D13 D14 C15",
|
||||
attrs=Attrs(PULLMODE="NONE", DRIVE="4", SLEWRATE="FAST", IO_TYPE="LVCMOS33")
|
||||
),
|
||||
|
||||
# Broadcom B50612D Gigabit Ethernet Transceiver
|
||||
Resource("eth_rgmii", 0,
|
||||
Subsignal("rst", PinsN("P4", dir="o")),
|
||||
Subsignal("mdc", Pins("N5", dir="o")),
|
||||
#Subsignal("mdio", Pins("P5", dir="io")),
|
||||
Subsignal("tx_clk", Pins("U19", dir="o")),
|
||||
Subsignal("tx_ctl", Pins("P19", dir="o")),
|
||||
Subsignal("tx_data", Pins("U20 T19 T20 R20", dir="o")),
|
||||
Subsignal("rx_clk", Pins("L19", dir="i")),
|
||||
Subsignal("rx_ctl", Pins("M20", dir="i")),
|
||||
Subsignal("rx_data", Pins("P20 N19 N20 M19", dir="i")),
|
||||
Attrs(IO_TYPE="LVCMOS33")
|
||||
),
|
||||
|
||||
# Broadcom B50612D Gigabit Ethernet Transceiver
|
||||
Resource("eth_rgmii", 1,
|
||||
Subsignal("rst", PinsN("P4", dir="o")),
|
||||
Subsignal("mdc", Pins("N5", dir="o")),
|
||||
#Subsignal("mdio", Pins("P5", dir="io")),
|
||||
Subsignal("tx_clk", Pins("G1", dir="o")),
|
||||
Subsignal("tx_ctl", Pins("K1", dir="o")),
|
||||
Subsignal("tx_data", Pins("G2 H1 J1 J3", dir="o")),
|
||||
Subsignal("rx_clk", Pins("H2", dir="i")),
|
||||
Subsignal("rx_ctl", Pins("P2", dir="i")),
|
||||
Subsignal("rx_data", Pins("K2 L1 N1 P1", dir="i")),
|
||||
Attrs(IO_TYPE="LVCMOS33")
|
||||
),
|
||||
|
||||
Resource("jtag", 0,
|
||||
Subsignal("trst", Pins("J17", dir="i")),
|
||||
Subsignal("tck", Pins("G18", dir="i")),
|
||||
Subsignal("tms", Pins("H16", dir="i")),
|
||||
Subsignal("tdo", Pins("H17", dir="o")),
|
||||
Subsignal("tdi", Pins("H18", dir="i")),
|
||||
Attrs(IO_TYPE="LVCMOS33")
|
||||
),
|
||||
|
||||
Resource("i2c", 0,
|
||||
Subsignal("sda", Pins("D16", dir="io")),
|
||||
Subsignal("scl", Pins("F5", dir="io")), # Hacky stuff for now, amlib needs it to be io for some reason
|
||||
Attrs(IO_TYPE="LVCMOS33")
|
||||
),
|
||||
]
|
||||
connectors = []
|
||||
# Connector("j", 1, "F3 F1 G3 - G2 H3 H5 F15 L2 K1 J5 K2 B16 J14 F12 -"),
|
||||
# Connector("j", 2, "J4 K3 G1 - K4 C2 E3 F15 L2 K1 J5 K2 B16 J14 F12 -"),
|
||||
# Connector("j", 3, "H4 K5 P1 - R1 L5 F2 F15 L2 K1 J5 K2 B16 J14 F12 -"),
|
||||
# Connector("j", 4, "P4 R2 M8 - M9 T6 R6 F15 L2 K1 J5 K2 B16 J14 F12 -"),
|
||||
# Connector("j", 5, "M11 N11 P12 - K15 N12 L16 F15 L2 K1 J5 K2 B16 J14 F12 -"),
|
||||
# Connector("j", 6, "K16 J15 J16 - J12 H15 G16 F15 L2 K1 J5 K2 B16 J14 F12 -"),
|
||||
# Connector("j", 7, "H13 J13 H12 - G14 H14 G15 F15 L2 K1 J5 K2 B16 J14 F12 -"),
|
||||
# Connector("j", 8, "A15 F16 A14 - E13 B14 A13 F15 L2 K1 J5 K2 B16 J14 F12 -"),
|
||||
# Connector("j", 19, " - M13 - - P11"),
|
||||
#]
|
||||
|
||||
@property
|
||||
def required_tools(self):
|
||||
return super().required_tools + [
|
||||
"ecpdap"
|
||||
]
|
||||
|
||||
def toolchain_prepare(self, fragment, name, **kwargs):
|
||||
overrides = dict(ecppack_opts="--compress")
|
||||
overrides.update(kwargs)
|
||||
return super().toolchain_prepare(fragment, name, **overrides)
|
||||
|
||||
def toolchain_program(self, products, name):
|
||||
tool = os.environ.get("ECPDAP", "ecpdap")
|
||||
with products.extract("{}.bit".format(name)) as bitstream_filename:
|
||||
subprocess.check_call([tool, "program", bitstream_filename, "--freq", "10M"])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
from .test.blinky import *
|
||||
Colorlight_i9_Platform().build(Blinky(), do_program=True)
|
@ -38,7 +38,7 @@ class UART(Elaboratable):
|
||||
#
|
||||
# Sets input/output baudrate to system clock / divisor. Resets to value
|
||||
# that provides 115200 baud rate. Writes to this register clear FIFOs.
|
||||
self.DIVISOR = Element(16, Element.Access.RW, name="DIVISOR")
|
||||
self.DIVISOR = Element(16, Element.Access.RW, name="UART_DIVISOR")
|
||||
|
||||
# Status register.
|
||||
#
|
||||
@ -47,22 +47,23 @@ class UART(Elaboratable):
|
||||
# [1]: txfifo_empty
|
||||
# [2]: rxfifo_full
|
||||
# [3]: rxfifo_empty
|
||||
self.SR = Element(4, Element.Access.R, name="SR")
|
||||
self.SR = Element(4, Element.Access.R, name="UART_SR")
|
||||
|
||||
# Data register.
|
||||
#
|
||||
# Writes push data into TX FIFO, and are discarded if full, reads pull
|
||||
# data from RX FIFO, and are invalid if it is empty. Incoming bytes are discarded
|
||||
# if the RX FIFO is full.
|
||||
self.DR = Element(8, Element.Access.RW, name="DR")
|
||||
self.DR = Element(8, Element.Access.RW, name="UART_DR")
|
||||
|
||||
# Set up CSR bus
|
||||
addr_width = ceil(log2(64))
|
||||
data_width = 32
|
||||
data_width = 8
|
||||
self._csr_mux = Multiplexer(addr_width=addr_width, data_width=data_width)
|
||||
self._csr_mux.add(self.DIVISOR)
|
||||
self._csr_mux.add(self.SR)
|
||||
self._csr_mux.add(self.DR)
|
||||
div_start, _stop = self._csr_mux.add(self.DIVISOR)
|
||||
sr_start, _stop = self._csr_mux.add(self.SR)
|
||||
dr_start, _stop = self._csr_mux.add(self.DR)
|
||||
print(f"UART added. DIVISOR 0x{div_start:x}, SR 0x{sr_start:x}, DR 0x{dr_start:x}")
|
||||
self.bus = self._csr_mux.bus
|
||||
|
||||
# Actual business logic
|
||||
@ -94,6 +95,7 @@ class UART(Elaboratable):
|
||||
m.submodules.csr_mux = self._csr_mux
|
||||
|
||||
# Hook up divisor to register.
|
||||
# TODO do some validation and write a known good value if a dumb value was provided
|
||||
m.d.comb += self.DIVISOR.r_data.eq(self._serial.divisor)
|
||||
with m.If(self.DIVISOR.w_stb):
|
||||
m.d.sync += self._serial.divisor.eq(self.DIVISOR.w_data)
|
||||
|
Loading…
Reference in New Issue
Block a user