Compare commits
9 Commits
9b49f1184e
...
05fed9e28e
Author | SHA1 | Date | |
---|---|---|---|
05fed9e28e | |||
5cfc562190 | |||
9dec411ff0 | |||
35a8841aa5 | |||
c5db01c70f | |||
c0b293e0c7 | |||
833db12d58 | |||
a864da5354 | |||
29ec5a8a43 |
1
firmware/Cargo.lock
generated
1
firmware/Cargo.lock
generated
@ -105,6 +105,7 @@ dependencies = [
|
|||||||
"defmt",
|
"defmt",
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
"panic-halt",
|
"panic-halt",
|
||||||
|
"riscv",
|
||||||
"riscv-rt",
|
"riscv-rt",
|
||||||
"smoltcp",
|
"smoltcp",
|
||||||
]
|
]
|
||||||
|
@ -7,6 +7,7 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
riscv-rt = "0.11.0"
|
riscv-rt = "0.11.0"
|
||||||
|
riscv = "0.10.1"
|
||||||
panic-halt = "0.2.0"
|
panic-halt = "0.2.0"
|
||||||
embedded-hal = "0.2.7"
|
embedded-hal = "0.2.7"
|
||||||
defmt = {version = "0.3.4", features = ["encoding-raw"] }
|
defmt = {version = "0.3.4", features = ["encoding-raw"] }
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
#!/usr/bin/sh
|
#!/usr/bin/sh
|
||||||
|
|
||||||
DEFMT_LOG=trace cargo build --release
|
DEFMT_LOG=trace cargo build --release
|
||||||
|
if [ $? -ne 0 ]
|
||||||
|
then
|
||||||
|
exit $?
|
||||||
|
fi
|
||||||
|
|
||||||
riscv64-unknown-elf-objcopy -S -O binary target/riscv32i-unknown-none-elf/release/fw fw.bin
|
riscv64-unknown-elf-objcopy -S -O binary target/riscv32i-unknown-none-elf/release/fw fw.bin
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
RAM : ORIGIN = 0x01100000, LENGTH = 4K
|
RAM : ORIGIN = 0x10000000, LENGTH = 64K
|
||||||
FLASH : ORIGIN = 0x01000000, LENGTH = 64K
|
FLASH : ORIGIN = 0x00000000, LENGTH = 64K
|
||||||
}
|
}
|
||||||
|
|
||||||
REGION_ALIAS("REGION_TEXT", FLASH);
|
REGION_ALIAS("REGION_TEXT", FLASH);
|
||||||
|
@ -14,26 +14,22 @@
|
|||||||
// - Slots are sized to ethernet MTU (1530), and addressed by the closest log2
|
// - Slots are sized to ethernet MTU (1530), and addressed by the closest log2
|
||||||
// thing, so 2048 bytes each
|
// thing, so 2048 bytes each
|
||||||
|
|
||||||
const LITEETH_BASE: u32 = 0x0300_0000;
|
|
||||||
|
|
||||||
const CTRL_RESET: u32 = 0x000;
|
|
||||||
const CTRL_SCRATCH: u32 = 0x004;
|
|
||||||
|
|
||||||
// Writer, or RX register blocks
|
// Writer, or RX register blocks
|
||||||
const ETHMAC_SRAM_WRITER_SLOT: u32 = 0x800;
|
const ETHMAC_SRAM_WRITER_SLOT: u32 = 0x000;
|
||||||
const ETHMAC_SRAM_WRITER_LENGTH: u32 = 0x804;
|
const ETHMAC_SRAM_WRITER_LENGTH: u32 = 0x004;
|
||||||
const ETHMAC_SRAM_WRITER_EV_STATUS: u32 = 0x80c;
|
const ETHMAC_SRAM_WRITER_EV_STATUS: u32 = 0x00c;
|
||||||
const ETHMAC_SRAM_WRITER_EV_PENDING: u32 = 0x810;
|
const ETHMAC_SRAM_WRITER_EV_PENDING: u32 = 0x010;
|
||||||
const ETHMAC_SRAM_WRITER_EV_ENABLE: u32 = 0x814;
|
const ETHMAC_SRAM_WRITER_EV_ENABLE: u32 = 0x014;
|
||||||
|
|
||||||
// Reader, or TX register blocks
|
// Reader, or TX register blocks
|
||||||
const ETHMAC_SRAM_READER_START: u32 = 0x818;
|
const ETHMAC_SRAM_READER_START: u32 = 0x018;
|
||||||
const ETHMAC_SRAM_READER_READY: u32 = 0x81c;
|
const ETHMAC_SRAM_READER_READY: u32 = 0x01c;
|
||||||
const ETHMAC_SRAM_READER_SLOT: u32 = 0x824;
|
const ETHMAC_SRAM_READER_LEVEL: u32 = 0x020;
|
||||||
const ETHMAC_SRAM_READER_LENGTH: u32 = 0x828;
|
const ETHMAC_SRAM_READER_SLOT: u32 = 0x024;
|
||||||
const ETHMAC_SRAM_READER_EV_STATUS: u32 = 0x82c;
|
const ETHMAC_SRAM_READER_LENGTH: u32 = 0x028;
|
||||||
const ETHMAC_SRAM_READER_EV_PENDING: u32 = 0x830;
|
const ETHMAC_SRAM_READER_EV_STATUS: u32 = 0x02c;
|
||||||
const ETHMAC_SRAM_READER_EV_ENABLE: u32 = 0x834;
|
const ETHMAC_SRAM_READER_EV_PENDING: u32 = 0x030;
|
||||||
|
const ETHMAC_SRAM_READER_EV_ENABLE: u32 = 0x014;
|
||||||
|
|
||||||
const NUM_RX_SLOTS: u32 = 2;
|
const NUM_RX_SLOTS: u32 = 2;
|
||||||
const NUM_TX_SLOTS: u32 = 2;
|
const NUM_TX_SLOTS: u32 = 2;
|
||||||
@ -46,52 +42,37 @@ use crate::uart::AmlibUart;
|
|||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
|
|
||||||
pub struct LiteEthDevice {
|
pub struct LiteEthDevice {
|
||||||
base_addr: u32,
|
csr_addr: u32,
|
||||||
|
ethmac_addr: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LiteEthTxToken {
|
pub struct LiteEthTxToken {
|
||||||
pub base_addr: u32,
|
pub csr_addr: u32,
|
||||||
|
pub ethmac_addr: u32,
|
||||||
pub slot: u32,
|
pub slot: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LiteEthRxToken {
|
pub struct LiteEthRxToken {
|
||||||
pub base_addr: u32,
|
pub csr_addr: u32,
|
||||||
|
pub ethmac_addr: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LiteEthDevice {
|
impl LiteEthDevice {
|
||||||
/// Initialises the device and returns an instance. Unsafe because there are
|
/// Initialises the device and returns an instance. Unsafe because there are
|
||||||
/// no checks for other users.
|
/// no checks for other users.
|
||||||
pub unsafe fn try_init(base_addr: u32) -> Option<Self> {
|
pub unsafe fn try_init(csr_addr: u32, ethmac_addr: u32) -> Option<Self> {
|
||||||
if !LiteEthDevice::check_wishbone_access(base_addr) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
// Reset liteeth
|
|
||||||
write_reg(base_addr + CTRL_RESET, 1u32);
|
|
||||||
busy_wait(200);
|
|
||||||
write_reg(base_addr + CTRL_RESET, 0u32);
|
|
||||||
busy_wait(200);
|
|
||||||
|
|
||||||
// Clear RX event to mark the slot as available
|
// Clear RX event to mark the slot as available
|
||||||
write_reg(base_addr + ETHMAC_SRAM_WRITER_EV_PENDING, 1u32);
|
write_reg(csr_addr + ETHMAC_SRAM_WRITER_EV_PENDING, 1u32);
|
||||||
|
|
||||||
// Clear TX event (unsure if necessary)
|
// Clear TX event (unsure if necessary)
|
||||||
write_reg(base_addr + ETHMAC_SRAM_READER_EV_PENDING, 1u32);
|
write_reg(csr_addr + ETHMAC_SRAM_READER_EV_PENDING, 1u32);
|
||||||
|
|
||||||
// Disable event interrupts, we poll, so no use for an interrupt
|
// Disable event interrupts, we poll, so no use for an interrupt
|
||||||
write_reg(base_addr + ETHMAC_SRAM_READER_EV_ENABLE, 0u32);
|
write_reg(csr_addr + ETHMAC_SRAM_READER_EV_ENABLE, 0u32);
|
||||||
write_reg(base_addr + ETHMAC_SRAM_WRITER_EV_ENABLE, 0u32);
|
write_reg(csr_addr + ETHMAC_SRAM_WRITER_EV_ENABLE, 0u32);
|
||||||
|
|
||||||
// Return a new device
|
// Return a new device
|
||||||
Some(Self { base_addr})
|
Some(Self { csr_addr, ethmac_addr })
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks that wishbone memory access is correct for the given base address
|
|
||||||
unsafe fn check_wishbone_access(base_addr: u32) -> bool {
|
|
||||||
// Read scratch register, which resets to 0x12345678
|
|
||||||
let value: u32 = read_reg(base_addr + CTRL_SCRATCH);
|
|
||||||
|
|
||||||
// If this isn't true, we screwed.
|
|
||||||
return value == 0x12345678;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,13 +88,13 @@ impl smoltcp::phy::Device for LiteEthDevice {
|
|||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// No data is available
|
// No data is available
|
||||||
if read_reg::<u32>(self.base_addr + ETHMAC_SRAM_WRITER_EV_STATUS) == 0 {
|
if read_reg::<u32>(self.csr_addr + ETHMAC_SRAM_WRITER_EV_STATUS) == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if TX slot 1 is available for the "return" packet
|
// Due to the fact that I can't check the status of an individual slot, I am going to just make sure
|
||||||
write_reg(self.base_addr + ETHMAC_SRAM_READER_SLOT, 1u32);
|
// level is 0 before I hand out any TX tokens
|
||||||
if read_reg::<u32>(self.base_addr + ETHMAC_SRAM_READER_READY) != 1 {
|
if read_reg::<u32>(self.csr_addr + ETHMAC_SRAM_READER_LEVEL) != 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,10 +104,12 @@ impl smoltcp::phy::Device for LiteEthDevice {
|
|||||||
defmt::trace!("RX Token given");
|
defmt::trace!("RX Token given");
|
||||||
Some((
|
Some((
|
||||||
LiteEthRxToken {
|
LiteEthRxToken {
|
||||||
base_addr: self.base_addr,
|
csr_addr: self.csr_addr,
|
||||||
|
ethmac_addr: self.ethmac_addr,
|
||||||
},
|
},
|
||||||
LiteEthTxToken {
|
LiteEthTxToken {
|
||||||
base_addr: self.base_addr,
|
csr_addr: self.csr_addr,
|
||||||
|
ethmac_addr: self.ethmac_addr,
|
||||||
slot: 1,
|
slot: 1,
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
@ -136,15 +119,17 @@ impl smoltcp::phy::Device for LiteEthDevice {
|
|||||||
fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option<Self::TxToken<'_>> {
|
fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option<Self::TxToken<'_>> {
|
||||||
// Check if slot 0 is ready, if so, return TxToken to slot 0
|
// Check if slot 0 is ready, if so, return TxToken to slot 0
|
||||||
unsafe {
|
unsafe {
|
||||||
write_reg(self.base_addr + ETHMAC_SRAM_READER_SLOT, 0u32);
|
// Due to the fact that I can't check the status of an individual slot, I am going to just make sure
|
||||||
if read_reg::<u32>(self.base_addr + ETHMAC_SRAM_READER_READY) == 0 {
|
// level is 0 before I hand out any TX tokens
|
||||||
|
if read_reg::<u32>(self.csr_addr + ETHMAC_SRAM_READER_LEVEL) != 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//writeln!(self.uart, "TX tkn").unwrap();
|
defmt::trace!("TX token given");
|
||||||
Some(LiteEthTxToken {
|
Some(LiteEthTxToken {
|
||||||
base_addr: self.base_addr,
|
csr_addr: self.csr_addr,
|
||||||
|
ethmac_addr: self.ethmac_addr,
|
||||||
slot: 0,
|
slot: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -167,23 +152,41 @@ impl smoltcp::phy::TxToken for LiteEthTxToken {
|
|||||||
where
|
where
|
||||||
F: FnOnce(&mut [u8]) -> R,
|
F: FnOnce(&mut [u8]) -> R,
|
||||||
{
|
{
|
||||||
// TODO 0x800 is ETHMAC offset, need to encode it somehow properly
|
let tx_slot_base: u32 = self.ethmac_addr + NUM_RX_SLOTS * SLOT_LEN;
|
||||||
let tx_slot_base: u32 = self.base_addr + 0x800 + NUM_RX_SLOTS * SLOT_LEN;
|
let tx_slot_addr = tx_slot_base + 0 * SLOT_LEN;
|
||||||
let tx_slot_addr = tx_slot_base + (self.slot as u32) * SLOT_LEN;
|
|
||||||
let tx_slot: &mut [u8] =
|
let tx_slot: &mut [u8] =
|
||||||
unsafe { core::slice::from_raw_parts_mut(tx_slot_addr as *mut u8, MTU) };
|
unsafe { core::slice::from_raw_parts_mut(tx_slot_addr as *mut u8, len) };
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
// Wait for it to be ready?
|
||||||
|
while read_reg::<u32>(self.csr_addr + ETHMAC_SRAM_READER_READY) == 0 {}
|
||||||
|
}
|
||||||
|
|
||||||
// Write data to buffer
|
// Write data to buffer
|
||||||
let res = f(tx_slot);
|
let res = f(tx_slot);
|
||||||
|
|
||||||
// Write length, and start sending data
|
// Write length, and start sending data
|
||||||
unsafe {
|
unsafe {
|
||||||
|
let level = read_reg::<u32>(self.csr_addr + ETHMAC_SRAM_READER_LEVEL);
|
||||||
|
defmt::trace!("level before sending: {}", level);
|
||||||
|
|
||||||
// set slot
|
// set slot
|
||||||
write_reg(self.base_addr + ETHMAC_SRAM_READER_SLOT, self.slot);
|
write_reg(self.csr_addr + ETHMAC_SRAM_READER_SLOT, 0u32);
|
||||||
// set length
|
// set length
|
||||||
write_reg(self.base_addr + ETHMAC_SRAM_READER_LENGTH, len as u32);
|
write_reg(self.csr_addr + ETHMAC_SRAM_READER_LENGTH, len as u32);
|
||||||
|
|
||||||
|
let slot = read_reg::<u32>(self.csr_addr + ETHMAC_SRAM_READER_SLOT);
|
||||||
|
let length = read_reg::<u32>(self.csr_addr + ETHMAC_SRAM_READER_LENGTH);
|
||||||
|
defmt::trace!("slot: {}, len: {}, addr: 0x{:08x}", slot, len, tx_slot_addr);
|
||||||
|
|
||||||
// send data
|
// send data
|
||||||
write_reg(self.base_addr + ETHMAC_SRAM_READER_START, 1u32);
|
write_reg(self.csr_addr + ETHMAC_SRAM_READER_START, 1u32);
|
||||||
|
|
||||||
|
let level = read_reg::<u32>(self.csr_addr + ETHMAC_SRAM_READER_LEVEL);
|
||||||
|
defmt::trace!("level after sending: {}", level);
|
||||||
|
|
||||||
|
// Clear event because why tf not
|
||||||
|
write_reg(self.csr_addr + ETHMAC_SRAM_READER_EV_PENDING, 1u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
res
|
||||||
@ -197,34 +200,24 @@ impl smoltcp::phy::RxToken for LiteEthRxToken {
|
|||||||
{
|
{
|
||||||
// Read the slot number
|
// Read the slot number
|
||||||
let slot = unsafe {
|
let slot = unsafe {
|
||||||
read_reg::<u32>(self.base_addr + ETHMAC_SRAM_WRITER_SLOT)
|
read_reg::<u32>(self.csr_addr + ETHMAC_SRAM_WRITER_SLOT)
|
||||||
};
|
};
|
||||||
// Read the available length
|
// Read the available length
|
||||||
let len = unsafe {
|
let len = unsafe {
|
||||||
read_reg::<u32>(self.base_addr + ETHMAC_SRAM_WRITER_LENGTH)
|
read_reg::<u32>(self.csr_addr + ETHMAC_SRAM_WRITER_LENGTH)
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO 0x800 is ETHMAC offset, need to encode it somehow properly
|
let rx_slot_addr: u32 = self.ethmac_addr + slot * SLOT_LEN;
|
||||||
let rx_slot_base: u32 = self.base_addr + 0x800 + SLOT_LEN;
|
|
||||||
let rx_slot_addr: u32 = rx_slot_base + slot * SLOT_LEN;
|
|
||||||
let rx_slot: &mut [u8] =
|
let rx_slot: &mut [u8] =
|
||||||
unsafe { core::slice::from_raw_parts_mut(rx_slot_addr as *mut u8, len as usize) };
|
unsafe { core::slice::from_raw_parts_mut(rx_slot_addr as *mut u8, len as usize) };
|
||||||
|
|
||||||
defmt::trace!("RX packet data. slot: {}, len: {}, addr: 0x{:08x}", slot, len, rx_slot_addr);
|
defmt::trace!("rx: len {}, addr: 0x{:08x}", len, rx_slot_addr);
|
||||||
for i in 0..16 {
|
|
||||||
let base = self.base_addr + i * 0x400;
|
|
||||||
defmt::trace!("Data at offset: 0x{:08x}", base);
|
|
||||||
for j in 0..32 {
|
|
||||||
defmt::trace!("byte {}: 0x{:x}", j, unsafe {read_reg::<u8>(base + j)});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read data from buffer
|
// Read data from buffer
|
||||||
let res = f(rx_slot);
|
let res = f(rx_slot);
|
||||||
|
|
||||||
// Clear event to mark slot as available
|
// Clear event to mark slot as available
|
||||||
unsafe {
|
unsafe {
|
||||||
write_reg(self.base_addr + ETHMAC_SRAM_WRITER_EV_PENDING, 1u32);
|
write_reg(self.csr_addr + ETHMAC_SRAM_WRITER_EV_PENDING, 1u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
res
|
res
|
||||||
|
45
firmware/src/litex_uart.rs
Normal file
45
firmware/src/litex_uart.rs
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
//! Quick and dirty LiteX uart drier
|
||||||
|
|
||||||
|
|
||||||
|
const REG_RXTX: u32 = 0;
|
||||||
|
const REG_TXFULL: u32 = 0x4;
|
||||||
|
//const REG_RXEMPTY: u32 = 0x8;
|
||||||
|
|
||||||
|
use crate::{write_reg, read_reg};
|
||||||
|
use core::fmt::Write;
|
||||||
|
|
||||||
|
pub enum Error {
|
||||||
|
TxFull,
|
||||||
|
RxEmpty,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LiteXUart {
|
||||||
|
base_addr: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LiteXUart {
|
||||||
|
pub fn new(base_addr: u32) -> Self{ Self {base_addr} }
|
||||||
|
|
||||||
|
pub fn try_put_char(&mut self, c: u8) -> Result<(), Error> {
|
||||||
|
unsafe {
|
||||||
|
if read_reg::<u32>(self.base_addr + REG_TXFULL) != 0 {
|
||||||
|
return Err(Error::TxFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
write_reg::<u32>(self.base_addr + REG_RXTX, c as u32);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for LiteXUart {
|
||||||
|
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(())
|
||||||
|
}
|
||||||
|
}
|
38
firmware/src/logging.rs
Normal file
38
firmware/src/logging.rs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
use core::{fmt::Write, any::Any};
|
||||||
|
|
||||||
|
use defmt;
|
||||||
|
|
||||||
|
use crate::uart::AmlibUart;
|
||||||
|
use core::arch::asm;
|
||||||
|
|
||||||
|
#[defmt::global_logger]
|
||||||
|
struct DefmtLogger;
|
||||||
|
|
||||||
|
unsafe impl defmt::Logger for DefmtLogger {
|
||||||
|
fn acquire() {
|
||||||
|
// Sync methods left empty because we don't use any interrupts
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn flush() {
|
||||||
|
// Sync methods left empty because we don't use any interrupts
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn release() {
|
||||||
|
// Sync methods left empty because we don't use any interrupts
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn write(bytes: &[u8]) {
|
||||||
|
//static mut UART: Option<AmlibUart> = None;
|
||||||
|
//if UART.is_none() {
|
||||||
|
// UART = Some(AmlibUart::new(0x0200_0040));
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
//let mut dev = UART.unwrap();
|
||||||
|
////writeln!(dev, "a").unwrap();
|
||||||
|
////writeln!(dev, "length: {}", bytes.len());
|
||||||
|
//for byte in bytes {
|
||||||
|
// while let Err(_) = dev.try_put_char(*byte) {}
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
@ -26,10 +26,30 @@ mod eth;
|
|||||||
mod i2c;
|
mod i2c;
|
||||||
mod mcp4726;
|
mod mcp4726;
|
||||||
mod uart;
|
mod uart;
|
||||||
|
mod litex_uart;
|
||||||
mod logging;
|
mod logging;
|
||||||
|
|
||||||
const MAC: [u8; 6] = [0xA0, 0xBB, 0xCC, 0xDD, 0xEE, 0xF0];
|
const MAC: [u8; 6] = [0xA0, 0xBB, 0xCC, 0xDD, 0xEE, 0xF0];
|
||||||
|
|
||||||
|
static mut SECONDS: u32 = 0;
|
||||||
|
|
||||||
|
/// External interrupt handler
|
||||||
|
#[export_name = "MachineExternal"]
|
||||||
|
fn external_interrupt_handler() {
|
||||||
|
let cause = riscv::register::mcause::read();
|
||||||
|
let mut uart = litex_uart::LiteXUart::new(0xf000_4000);
|
||||||
|
writeln!(uart, "mcause: {}", cause.bits());
|
||||||
|
|
||||||
|
if (cause.is_interrupt()) {
|
||||||
|
let mut uart = litex_uart::LiteXUart::new(0xf000_4000);
|
||||||
|
writeln!(uart, "mcause: {}", cause.code());
|
||||||
|
|
||||||
|
if cause.code() == 1 {
|
||||||
|
// TIMER0 event, we have reset so count another second
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// use `main` as the entry point of this application
|
// use `main` as the entry point of this application
|
||||||
// `main` is not allowed to return
|
// `main` is not allowed to return
|
||||||
#[entry]
|
#[entry]
|
||||||
@ -44,9 +64,13 @@ fn main() -> ! {
|
|||||||
// }
|
// }
|
||||||
//};
|
//};
|
||||||
let blink_period = 10_000_000u32;
|
let blink_period = 10_000_000u32;
|
||||||
let mut uart = uart::AmlibUart::new(0x0200_0040);
|
let mut uart = litex_uart::LiteXUart::new(0xf000_4000);
|
||||||
|
writeln!(uart, "uart init");
|
||||||
|
|
||||||
let mut device = unsafe { eth::LiteEthDevice::try_init(0x0300_0000).unwrap() };
|
// enable timer
|
||||||
|
|
||||||
|
let mut device = unsafe { eth::LiteEthDevice::try_init(0xf000_0800, 0x8000_0000).unwrap() };
|
||||||
|
writeln!(uart, "eth init");
|
||||||
|
|
||||||
use smoltcp::wire::{EthernetAddress, HardwareAddress};
|
use smoltcp::wire::{EthernetAddress, HardwareAddress};
|
||||||
let mut config = smoltcp::iface::Config::default();
|
let mut config = smoltcp::iface::Config::default();
|
||||||
@ -74,7 +98,25 @@ fn main() -> ! {
|
|||||||
|
|
||||||
let mut last_blink: u32 = 0;
|
let mut last_blink: u32 = 0;
|
||||||
let mut toggle = false;
|
let mut toggle = false;
|
||||||
defmt::info!("Done setup");
|
//defmt::info!("Done setup");
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
//riscv::interrupt::enable();
|
||||||
|
//riscv::register::mie::set_mext();
|
||||||
|
//riscv::register::mie::set_msoft();
|
||||||
|
|
||||||
|
// Enable UART rx event for test
|
||||||
|
//write_reg(0xf000_4014, 1u32);
|
||||||
|
|
||||||
|
// Timer stuff
|
||||||
|
write_reg(0xf000_3808, 0u32); // Disable timer
|
||||||
|
write_reg(0xf000_3800, 0u32); // Set LOAD value
|
||||||
|
write_reg(0xf000_3804, 60_000_000u32); // Set RELOAD value
|
||||||
|
write_reg(0xf000_3808, 1u32); // Enable timer
|
||||||
|
|
||||||
|
// Enable timer event
|
||||||
|
//write_reg(0xf000_381c, 1u32);
|
||||||
|
}
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let now = millis();
|
let now = millis();
|
||||||
@ -82,26 +124,47 @@ fn main() -> ! {
|
|||||||
last_blink = now;
|
last_blink = now;
|
||||||
toggle = !toggle;
|
toggle = !toggle;
|
||||||
write_led(if toggle { 1 } else { 0 });
|
write_led(if toggle { 1 } else { 0 });
|
||||||
|
|
||||||
|
let val: u32 = unsafe {read_reg(0x8000_2000)};
|
||||||
|
writeln!(uart, "Sampler value: 0x{:08x}", val).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
if iface.poll(Instant::from_millis(now), &mut device, &mut socket_set) {
|
if iface.poll(Instant::from_millis(now), &mut device, &mut socket_set) {
|
||||||
//writeln!(uart, "iface did something");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handle_timer_event();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn busy_wait(ms: u32) {
|
fn handle_timer_event() {
|
||||||
let start = millis();
|
unsafe {
|
||||||
while millis() - start < ms {
|
if read_reg::<u32>(0xf000_3818) == 0 {
|
||||||
unsafe {
|
return;
|
||||||
asm!("nop");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear TIMER0 event status, and update time
|
||||||
|
write_reg(0xf000_3818, 1u32);
|
||||||
|
SECONDS += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
fn busy_wait(ms: u32) {
|
||||||
|
//let start = millis();
|
||||||
|
//while millis() - start < ms {
|
||||||
|
// unsafe {
|
||||||
|
// asm!("nop");
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
for i in 0..ms*20_000 {
|
||||||
|
unsafe {asm!("nop");}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_led(val: u32) {
|
fn write_led(val: u32) {
|
||||||
unsafe {
|
unsafe {
|
||||||
write_reg(0x01200000, val);
|
write_reg(0xf000_2000, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,5 +177,14 @@ unsafe fn read_reg<T>(addr: u32) -> T {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn millis() -> u32 {
|
fn millis() -> u32 {
|
||||||
unsafe { read_reg(0x01300000) }
|
riscv::interrupt::free(|| {
|
||||||
|
unsafe {
|
||||||
|
// Latch timer value
|
||||||
|
write_reg(0xf000_380c, 1u32);
|
||||||
|
// Read timer value
|
||||||
|
let val: u32 = read_reg(0xf000_3810);
|
||||||
|
let val = 60_000_000 - val;
|
||||||
|
(SECONDS * 1000) + val / 60_000
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ from amaranth import *
|
|||||||
from amaranth.lib.io import pin_layout
|
from amaranth.lib.io import pin_layout
|
||||||
from amaranth_soc.wishbone.bus import Interface
|
from amaranth_soc.wishbone.bus import Interface
|
||||||
from amaranth_soc.memory import MemoryMap
|
from amaranth_soc.memory import MemoryMap
|
||||||
|
from math import log2, ceil
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["LiteEth", "rgmii_layout"]
|
__all__ = ["LiteEth", "rgmii_layout"]
|
||||||
@ -12,10 +13,11 @@ class LiteEth(Elaboratable, Interface):
|
|||||||
def __init__(self, eth_interface):
|
def __init__(self, eth_interface):
|
||||||
self.eth_interface = eth_interface
|
self.eth_interface = eth_interface
|
||||||
|
|
||||||
# Addr width is 13 bits to accomodate 0x1FFF, which is well past what we care about
|
# Highest address to support is 0x0002_1FFF, so need 18 bits of full address
|
||||||
Interface.__init__(self, addr_width=15, data_width=32, granularity=8, features=["cti", "bte", "err"])
|
highest_addr = 0x0002_1FFF
|
||||||
# TODO I need to understand the semantics here better
|
bit_width = ceil(log2(highest_addr))
|
||||||
memory_map = MemoryMap(addr_width=17, data_width=8)
|
Interface.__init__(self, addr_width=bit_width - 2, data_width=32, granularity=8, features=["cti", "bte", "err"])
|
||||||
|
memory_map = MemoryMap(addr_width=bit_width, data_width=8)
|
||||||
#memory_map.add_resource(self, name="LiteETH", size=0x2000)
|
#memory_map.add_resource(self, name="LiteETH", size=0x2000)
|
||||||
self.memory_map = memory_map
|
self.memory_map = memory_map
|
||||||
|
|
||||||
@ -31,7 +33,6 @@ class LiteEth(Elaboratable, Interface):
|
|||||||
m = Module()
|
m = Module()
|
||||||
|
|
||||||
|
|
||||||
# TODO I have to provide TX/RX clocks myself
|
|
||||||
core = Instance(
|
core = Instance(
|
||||||
"liteeth_core",
|
"liteeth_core",
|
||||||
i_sys_clock=ClockSignal(),
|
i_sys_clock=ClockSignal(),
|
||||||
@ -81,8 +82,6 @@ rgmii_layout = [
|
|||||||
("rst", pin_layout(1, "o")),
|
("rst", pin_layout(1, "o")),
|
||||||
("int_n", pin_layout(1, "i")),
|
("int_n", pin_layout(1, "i")),
|
||||||
|
|
||||||
# TODO is this not IO? why does LiteEth say input?
|
|
||||||
# I think the answer is it uses a primitive, not 100% right now
|
|
||||||
("mdio", pin_layout(1, "io")),
|
("mdio", pin_layout(1, "io")),
|
||||||
("mdc", pin_layout(1, "o")),
|
("mdc", pin_layout(1, "o")),
|
||||||
("rx_ctl", pin_layout(1, "i")),
|
("rx_ctl", pin_layout(1, "i")),
|
||||||
|
199
gateware/litex_main.py
Executable file
199
gateware/litex_main.py
Executable file
@ -0,0 +1,199 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
#
|
||||||
|
# This file is part of LiteX-Boards.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2021 Kazumoto Kojima <kkojima@rr.iij4u.or.jp>
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
from migen import *
|
||||||
|
|
||||||
|
from litex.build.io import DDROutput
|
||||||
|
|
||||||
|
#from litex_boards.platforms import colorlight_i5
|
||||||
|
from platforms import sonar as colorlight_i5
|
||||||
|
|
||||||
|
from litex.build.lattice.trellis import trellis_args, trellis_argdict
|
||||||
|
|
||||||
|
from litex.soc.cores.clock import *
|
||||||
|
from litex.soc.integration.soc_core import *
|
||||||
|
from litex.soc.integration.builder import *
|
||||||
|
from litex.soc.cores.video import VideoHDMIPHY
|
||||||
|
from litex.soc.cores.led import LedChaser
|
||||||
|
|
||||||
|
from litex.soc.interconnect.csr import *
|
||||||
|
|
||||||
|
from litedram.modules import M12L64322A # Compatible with EM638325-6H.
|
||||||
|
from litedram.phy import GENSDRPHY, HalfRateGENSDRPHY
|
||||||
|
|
||||||
|
from liteeth.phy.ecp5rgmii import LiteEthPHYRGMII
|
||||||
|
|
||||||
|
from sampler import Sampler
|
||||||
|
from litex.soc.integration.soc import SoCRegion
|
||||||
|
|
||||||
|
# CRG ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class _CRG(Module):
|
||||||
|
def __init__(self, platform, sys_clk_freq, use_internal_osc=False, with_usb_pll=False, with_video_pll=False, sdram_rate="1:1"):
|
||||||
|
self.rst = Signal()
|
||||||
|
self.clock_domains.cd_sys = ClockDomain()
|
||||||
|
if sdram_rate == "1:2":
|
||||||
|
self.clock_domains.cd_sys2x = ClockDomain()
|
||||||
|
self.clock_domains.cd_sys2x_ps = ClockDomain()
|
||||||
|
else:
|
||||||
|
self.clock_domains.cd_sys_ps = ClockDomain()
|
||||||
|
|
||||||
|
# # #
|
||||||
|
|
||||||
|
# Clk / Rst
|
||||||
|
if not use_internal_osc:
|
||||||
|
clk = platform.request("clk25")
|
||||||
|
clk_freq = 25e6
|
||||||
|
else:
|
||||||
|
clk = Signal()
|
||||||
|
div = 5
|
||||||
|
self.specials += Instance("OSCG",
|
||||||
|
p_DIV = div,
|
||||||
|
o_OSC = clk
|
||||||
|
)
|
||||||
|
clk_freq = 310e6/div
|
||||||
|
|
||||||
|
#rst_n = platform.request("cpu_reset_n")
|
||||||
|
|
||||||
|
# PLL
|
||||||
|
self.submodules.pll = pll = ECP5PLL()
|
||||||
|
self.comb += pll.reset.eq(self.rst)
|
||||||
|
pll.register_clkin(clk, clk_freq)
|
||||||
|
pll.create_clkout(self.cd_sys, sys_clk_freq)
|
||||||
|
if sdram_rate == "1:2":
|
||||||
|
pll.create_clkout(self.cd_sys2x, 2*sys_clk_freq)
|
||||||
|
pll.create_clkout(self.cd_sys2x_ps, 2*sys_clk_freq, phase=180) # Idealy 90° but needs to be increased.
|
||||||
|
else:
|
||||||
|
pll.create_clkout(self.cd_sys_ps, sys_clk_freq, phase=180) # Idealy 90° but needs to be increased.
|
||||||
|
|
||||||
|
# SDRAM clock
|
||||||
|
sdram_clk = ClockSignal("sys2x_ps" if sdram_rate == "1:2" else "sys_ps")
|
||||||
|
self.specials += DDROutput(1, 0, platform.request("sdram_clock"), sdram_clk)
|
||||||
|
|
||||||
|
# BaseSoC ------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# TODO make my own platform for this based on the colorlight one, so I can export I2C and other pins
|
||||||
|
class BaseSoC(SoCCore):
|
||||||
|
def __init__(self, sys_clk_freq=60e6, eth_phy=0, with_led_chaser=True, use_internal_osc=False,
|
||||||
|
sdram_rate="1:1", with_video_terminal=False, with_video_framebuffer=False,
|
||||||
|
**kwargs):
|
||||||
|
|
||||||
|
# TODO change SRAM size
|
||||||
|
kwargs["integrated_sram_size"] = 64 * 1024
|
||||||
|
kwargs["integrated_rom_init"] = "../firmware/fw.bin"
|
||||||
|
|
||||||
|
platform = colorlight_i5.Platform(board="i9", revision="7.2", toolchain="trellis")
|
||||||
|
|
||||||
|
# CRG --------------------------------------------------------------------------------------
|
||||||
|
with_usb_pll = kwargs.get("uart_name", None) == "usb_acm"
|
||||||
|
with_video_pll = with_video_terminal or with_video_framebuffer
|
||||||
|
self.submodules.crg = _CRG(platform, sys_clk_freq,
|
||||||
|
use_internal_osc = use_internal_osc,
|
||||||
|
with_usb_pll = with_usb_pll,
|
||||||
|
with_video_pll = with_video_pll,
|
||||||
|
sdram_rate = sdram_rate
|
||||||
|
)
|
||||||
|
|
||||||
|
# SoCCore ----------------------------------------------------------------------------------
|
||||||
|
SoCCore.__init__(self, platform, int(sys_clk_freq), ident = "LiteX SoC on Sonar FPGA", **kwargs)
|
||||||
|
|
||||||
|
# Leds -------------------------------------------------------------------------------------
|
||||||
|
if with_led_chaser:
|
||||||
|
ledn = platform.request_all("user_led_n")
|
||||||
|
self.submodules.leds = LedChaser(pads=ledn, sys_clk_freq=sys_clk_freq)
|
||||||
|
|
||||||
|
# SPI Flash --------------------------------------------------------------------------------
|
||||||
|
from litespi.modules import W25Q64 as SpiFlashModule
|
||||||
|
|
||||||
|
from litespi.opcodes import SpiNorFlashOpCodes as Codes
|
||||||
|
self.add_spi_flash(mode="1x", module=SpiFlashModule(Codes.READ_1_1_1))
|
||||||
|
|
||||||
|
# SDR SDRAM --------------------------------------------------------------------------------
|
||||||
|
#if not self.integrated_main_ram_size:
|
||||||
|
# sdrphy_cls = HalfRateGENSDRPHY if sdram_rate == "1:2" else GENSDRPHY
|
||||||
|
# self.submodules.sdrphy = sdrphy_cls(platform.request("sdram"))
|
||||||
|
# self.add_sdram("sdram",
|
||||||
|
# phy = self.sdrphy,
|
||||||
|
# module = M12L64322A(sys_clk_freq, sdram_rate),
|
||||||
|
# l2_cache_size = kwargs.get("l2_size", 8192)
|
||||||
|
# )
|
||||||
|
|
||||||
|
# Ethernet / Etherbone ---------------------------------------------------------------------
|
||||||
|
self.submodules.ethphy = LiteEthPHYRGMII(
|
||||||
|
clock_pads = self.platform.request("eth_clocks", eth_phy),
|
||||||
|
pads = self.platform.request("eth", eth_phy),
|
||||||
|
tx_delay = 0)
|
||||||
|
self.add_ethernet(phy=self.ethphy)
|
||||||
|
|
||||||
|
# Video ------------------------------------------------------------------------------------
|
||||||
|
if with_video_terminal or with_video_framebuffer:
|
||||||
|
self.submodules.videophy = VideoHDMIPHY(platform.request("gpdi"), clock_domain="hdmi")
|
||||||
|
if with_video_terminal:
|
||||||
|
self.add_video_terminal(phy=self.videophy, timings="800x600@60Hz", clock_domain="hdmi")
|
||||||
|
if with_video_framebuffer:
|
||||||
|
self.add_video_framebuffer(phy=self.videophy, timings="800x600@60Hz", clock_domain="hdmi")
|
||||||
|
|
||||||
|
self.submodules.sampler = Sampler(platform.request("adc"))
|
||||||
|
sampler_region = SoCRegion(origin=None, size=0x1000, cached=False)
|
||||||
|
#self.add_wb_slave(0x9000_0000, self.sampler.bus, 0x1000)
|
||||||
|
# TODO better way to do this?
|
||||||
|
self.bus.add_slave(name="sampler", slave=self.sampler.bus, region=sampler_region)
|
||||||
|
|
||||||
|
# Build --------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def main():
|
||||||
|
from litex.soc.integration.soc import LiteXSoCArgumentParser
|
||||||
|
parser = LiteXSoCArgumentParser(description="LiteX SoC on Colorlight I5")
|
||||||
|
target_group = parser.add_argument_group(title="Target options")
|
||||||
|
target_group.add_argument("--build", action="store_true", help="Build design.")
|
||||||
|
target_group.add_argument("--load", action="store_true", help="Load bitstream.")
|
||||||
|
target_group.add_argument("--sys-clk-freq", default=60e6, help="System clock frequency.")
|
||||||
|
sdopts = target_group.add_mutually_exclusive_group()
|
||||||
|
sdopts.add_argument("--with-spi-sdcard", action="store_true", help="Enable SPI-mode SDCard support.")
|
||||||
|
sdopts.add_argument("--with-sdcard", action="store_true", help="Enable SDCard support.")
|
||||||
|
target_group.add_argument("--eth-phy", default=0, type=int, help="Ethernet PHY (0 or 1).")
|
||||||
|
target_group.add_argument("--use-internal-osc", action="store_true", help="Use internal oscillator.")
|
||||||
|
target_group.add_argument("--sdram-rate", default="1:1", help="SDRAM Rate (1:1 Full Rate or 1:2 Half Rate).")
|
||||||
|
viopts = target_group.add_mutually_exclusive_group()
|
||||||
|
viopts.add_argument("--with-video-terminal", action="store_true", help="Enable Video Terminal (HDMI).")
|
||||||
|
viopts.add_argument("--with-video-framebuffer", action="store_true", help="Enable Video Framebuffer (HDMI).")
|
||||||
|
builder_args(parser)
|
||||||
|
soc_core_args(parser)
|
||||||
|
trellis_args(parser)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# Build firmware
|
||||||
|
import subprocess as sp
|
||||||
|
sp.run(["./build_and_strip.sh"], cwd="../firmware").check_returncode()
|
||||||
|
|
||||||
|
soc = BaseSoC(
|
||||||
|
sys_clk_freq = int(float(args.sys_clk_freq)),
|
||||||
|
eth_phy = args.eth_phy,
|
||||||
|
use_internal_osc = args.use_internal_osc,
|
||||||
|
sdram_rate = args.sdram_rate,
|
||||||
|
with_video_terminal = args.with_video_terminal,
|
||||||
|
with_video_framebuffer = args.with_video_framebuffer,
|
||||||
|
**soc_core_argdict(args)
|
||||||
|
)
|
||||||
|
soc.platform.add_extension(colorlight_i5._sdcard_pmod_io)
|
||||||
|
if args.with_spi_sdcard:
|
||||||
|
soc.add_spi_sdcard()
|
||||||
|
if args.with_sdcard:
|
||||||
|
soc.add_sdcard()
|
||||||
|
|
||||||
|
builder = Builder(soc, **builder_argdict(args))
|
||||||
|
builder_kargs = trellis_argdict(args)
|
||||||
|
if args.build:
|
||||||
|
builder.build(**builder_kargs)
|
||||||
|
|
||||||
|
if args.load:
|
||||||
|
prog = soc.platform.create_programmer()
|
||||||
|
prog.load_bitstream(builder.get_bitstream_filename(mode="sram"))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
233
gateware/platforms/sonar.py
Normal file
233
gateware/platforms/sonar.py
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
"""
|
||||||
|
LiteX Platform for sonar board using Colorlight i9 module
|
||||||
|
"""
|
||||||
|
|
||||||
|
# This file used to belong to LiteX Boards
|
||||||
|
#
|
||||||
|
# Copyright (c) 2021 Kazumoto Kojima <kkojima@rr.iij4u.or.jp>
|
||||||
|
# Copyright (c) 2023 David Lenfesty <lenfesty@ualberta.ca>
|
||||||
|
# SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
|
||||||
|
# The Colorlight i5 PCB and IOs have been documented by @wuxx
|
||||||
|
# https://github.com/wuxx/Colorlight-FPGA-Projects
|
||||||
|
|
||||||
|
import copy
|
||||||
|
|
||||||
|
from litex.build.generic_platform import *
|
||||||
|
from litex.build.lattice import LatticePlatform
|
||||||
|
from litex.build.lattice.programmer import EcpDapProgrammer
|
||||||
|
|
||||||
|
# IOs ----------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_io_v7_0 = [ # Documented by @smunaut
|
||||||
|
# Clk
|
||||||
|
("clk25", 0, Pins("P3"), IOStandard("LVCMOS33")),
|
||||||
|
|
||||||
|
# Led
|
||||||
|
("user_led_n", 0, Pins("U16"), IOStandard("LVCMOS33")),
|
||||||
|
|
||||||
|
# Serial
|
||||||
|
("serial", 0,
|
||||||
|
Subsignal("tx", Pins("P16")),
|
||||||
|
Subsignal("rx", Pins("L5")),
|
||||||
|
IOStandard("LVCMOS33")
|
||||||
|
),
|
||||||
|
|
||||||
|
# TODO the other serial ports
|
||||||
|
|
||||||
|
# TODO I2C
|
||||||
|
|
||||||
|
# SPIFlash (GD25Q16CSIG)
|
||||||
|
("spiflash", 0,
|
||||||
|
Subsignal("cs_n", Pins("R2")),
|
||||||
|
# https://github.com/m-labs/nmigen-boards/pull/38
|
||||||
|
#Subsignal("clk", Pins("")), driven through USRMCLK
|
||||||
|
Subsignal("mosi", Pins("W2")),
|
||||||
|
Subsignal("miso", Pins("V2")),
|
||||||
|
IOStandard("LVCMOS33"),
|
||||||
|
),
|
||||||
|
|
||||||
|
# SDRAM SDRAM (EM638325-6H)
|
||||||
|
("sdram_clock", 0, Pins("B9"), IOStandard("LVCMOS33")),
|
||||||
|
("sdram", 0,
|
||||||
|
Subsignal("a", Pins(
|
||||||
|
"B13 C14 A16 A17 B16 B15 A14 A13",
|
||||||
|
"A12 A11 B12")),
|
||||||
|
Subsignal("dq", Pins(
|
||||||
|
"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")),
|
||||||
|
Subsignal("we_n", Pins("A10")),
|
||||||
|
Subsignal("ras_n", Pins("B10")),
|
||||||
|
Subsignal("cas_n", Pins("A9")),
|
||||||
|
#Subsignal("cs_n", Pins("")), # gnd
|
||||||
|
#Subsignal("cke", Pins("")), # 3v3
|
||||||
|
Subsignal("ba", Pins("B11 C8")), # sdram pin BA0 and BA1
|
||||||
|
#Subsignal("dm", Pins("")), # gnd
|
||||||
|
IOStandard("LVCMOS33"),
|
||||||
|
Misc("SLEWRATE=FAST")
|
||||||
|
),
|
||||||
|
|
||||||
|
# RGMII Ethernet (B50612D)
|
||||||
|
# The order of the two PHYs is swapped with the naming of the connectors
|
||||||
|
# on the board so to match with the configuration of their PHYA[0] pins.
|
||||||
|
("eth_clocks", 0,
|
||||||
|
Subsignal("tx", Pins("G1")),
|
||||||
|
Subsignal("rx", Pins("H2")),
|
||||||
|
IOStandard("LVCMOS33")
|
||||||
|
),
|
||||||
|
("eth", 0,
|
||||||
|
Subsignal("rst_n", Pins("P4")),
|
||||||
|
Subsignal("mdio", Pins("P5")),
|
||||||
|
Subsignal("mdc", Pins("N5")),
|
||||||
|
Subsignal("rx_ctl", Pins("P2")),
|
||||||
|
Subsignal("rx_data", Pins("K2 L1 N1 P1")),
|
||||||
|
Subsignal("tx_ctl", Pins("K1")),
|
||||||
|
Subsignal("tx_data", Pins("G2 H1 J1 J3")),
|
||||||
|
IOStandard("LVCMOS33")
|
||||||
|
),
|
||||||
|
("eth_clocks", 1,
|
||||||
|
Subsignal("tx", Pins("U19")),
|
||||||
|
Subsignal("rx", Pins("L19")),
|
||||||
|
IOStandard("LVCMOS33")
|
||||||
|
),
|
||||||
|
("eth", 1,
|
||||||
|
Subsignal("rst_n", Pins("P4")),
|
||||||
|
Subsignal("mdio", Pins("P5")),
|
||||||
|
Subsignal("mdc", Pins("N5")),
|
||||||
|
Subsignal("rx_ctl", Pins("M20")),
|
||||||
|
Subsignal("rx_data", Pins("P20 N19 N20 M19")),
|
||||||
|
Subsignal("tx_ctl", Pins("P19")),
|
||||||
|
Subsignal("tx_data", Pins("U20 T19 T20 R20")),
|
||||||
|
IOStandard("LVCMOS33")
|
||||||
|
),
|
||||||
|
|
||||||
|
# GPDI
|
||||||
|
("gpdi", 0,
|
||||||
|
Subsignal("clk_p", Pins("J19"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
|
||||||
|
#Subsignal("clk_n", Pins("K19"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
|
||||||
|
Subsignal("data0_p", Pins("G19"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
|
||||||
|
#Subsignal("data0_n", Pins("H20"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
|
||||||
|
Subsignal("data1_p", Pins("E20"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
|
||||||
|
#Subsignal("data1_n", Pins("F19"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
|
||||||
|
Subsignal("data2_p", Pins("C20"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
|
||||||
|
#Subsignal("data2_n", Pins("D19"), IOStandard("LVCMOS33D"), Misc("DRIVE=4")),
|
||||||
|
),
|
||||||
|
|
||||||
|
# High speed parallel ADCs
|
||||||
|
("adc", 0,
|
||||||
|
Subsignal("data", Pins("M18 N18 N17 P18 U17 U18 T17 M17 P17 R17")),
|
||||||
|
# TODO ???? what other pins are changed in 7.2
|
||||||
|
Subsignal("refclk", Pins("L2")),
|
||||||
|
Subsignal("oen_b", Pins("K18")),
|
||||||
|
Subsignal("standby", Pins("C18")),
|
||||||
|
Subsignal("dfs", Pins("T18")),
|
||||||
|
Subsignal("otr", Pins("R18")),
|
||||||
|
IOStandard("LVCMOS33")
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
# From https://github.com/wuxx/Colorlight-FPGA-Projects/blob/master/schematic/i5_v6.0-extboard.pdf and
|
||||||
|
# https://github.com/wuxx/Colorlight-FPGA-Projects/blob/master/doc/i5_extboard_v1.2_pinout.png
|
||||||
|
_connectors_v7_0 = [
|
||||||
|
("pmode", "C17 B18 B20 F20 A18 A19 B19 D20"),
|
||||||
|
("pmodf", "D1 C1 C2 E3 E2 D2 B1 A3"),
|
||||||
|
]
|
||||||
|
|
||||||
|
# ColorLight i9 V 7.2 hardware
|
||||||
|
# See https://github.com/wuxx/Colorlight-FPGA-Projects/blob/master/colorlight_i9_v7.2.md
|
||||||
|
|
||||||
|
# SPIFlash (W25Q64JVSIQ)
|
||||||
|
|
||||||
|
_io_v7_2 = copy.deepcopy(_io_v7_0)
|
||||||
|
|
||||||
|
# Change the LED pin to "L2"
|
||||||
|
|
||||||
|
for i, x in enumerate(_io_v7_2):
|
||||||
|
if x[:2] == ("user_led_n", 0):
|
||||||
|
# TODO fix in HW
|
||||||
|
#_io_v7_2[i] = ("user_led_n", 0, Pins("L2"), IOStandard("LVCMOS33"))
|
||||||
|
_io_v7_2[i] = ("user_led_n", 0, Pins("J19"), IOStandard("LVCMOS33"))
|
||||||
|
break
|
||||||
|
|
||||||
|
# optional, alternative uart location
|
||||||
|
# requires "--uart-name serialx"
|
||||||
|
_io_v7_2 += [
|
||||||
|
("serialx", 0, Subsignal("tx", Pins("E5")), Subsignal("rx", Pins("F4")), IOStandard("LVCMOS33"))
|
||||||
|
]
|
||||||
|
|
||||||
|
_connectors_v7_2 = copy.deepcopy(_connectors_v7_0)
|
||||||
|
|
||||||
|
# Append the rest of the pmod interfaces
|
||||||
|
|
||||||
|
_connectors_v7_2 += [
|
||||||
|
# P2
|
||||||
|
("pmodc", "P17 R18 C18 L2 M17 R17 T18 K18"),
|
||||||
|
("pmodd", "J20 L18 M18 N17 G20 K20 L20 N18"),
|
||||||
|
# P4
|
||||||
|
("pmodg", "H4 G3 F1 F2 H3 F3 E4 E1"),
|
||||||
|
("pmodh", "- E19 B3 K5 - B2 K4 A2"),
|
||||||
|
# P5
|
||||||
|
("pmodi", "D18 G5 F5 E5 D17 D16 E6 F4"),
|
||||||
|
("pmodj", "J17 H17 H16 G16 H18 G18 F18 E18"),
|
||||||
|
# P6
|
||||||
|
("pmodk", "R3 M4 L5 J16 N4 L4 P16 J18"),
|
||||||
|
("pmodl", "R1 U1 W1 M1 T1 Y2 V1 N2"),
|
||||||
|
]
|
||||||
|
|
||||||
|
# PMODS --------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def sdcard_pmod_io(pmod):
|
||||||
|
return [
|
||||||
|
# SDCard PMOD:
|
||||||
|
# - https://store.digilentinc.com/pmod-microsd-microsd-card-slot/
|
||||||
|
("spisdcard", 0,
|
||||||
|
Subsignal("clk", Pins(f"{pmod}:3")),
|
||||||
|
Subsignal("mosi", Pins(f"{pmod}:1"), Misc("PULLMODE=UP")),
|
||||||
|
Subsignal("cs_n", Pins(f"{pmod}:0"), Misc("PULLMODE=UP")),
|
||||||
|
Subsignal("miso", Pins(f"{pmod}:2"), Misc("PULLMODE=UP")),
|
||||||
|
Misc("SLEWRATE=FAST"),
|
||||||
|
IOStandard("LVCMOS33"),
|
||||||
|
),
|
||||||
|
("sdcard", 0,
|
||||||
|
Subsignal("data", Pins(f"{pmod}:2 {pmod}:4 {pmod}:5 {pmod}:0"), Misc("PULLMODE=UP")),
|
||||||
|
Subsignal("cmd", Pins(f"{pmod}:1"), Misc("PULLMODE=UP")),
|
||||||
|
Subsignal("clk", Pins(f"{pmod}:3")),
|
||||||
|
Subsignal("cd", Pins(f"{pmod}:6")),
|
||||||
|
#Misc("SLEWRATE=FAST"),
|
||||||
|
IOStandard("LVCMOS33"),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
_sdcard_pmod_io = sdcard_pmod_io("pmode") # SDCARD PMOD on P3.
|
||||||
|
|
||||||
|
# Platform -----------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class Platform(LatticePlatform):
|
||||||
|
default_clk_name = "clk25"
|
||||||
|
default_clk_period = 1e9/25e6
|
||||||
|
|
||||||
|
def __init__(self, board="i5", revision="7.0", toolchain="trellis"):
|
||||||
|
if board == "i5":
|
||||||
|
assert revision in ["7.0"]
|
||||||
|
self.revision = revision
|
||||||
|
device = {"7.0": "LFE5U-25F-6BG381C"}[revision]
|
||||||
|
io = {"7.0": _io_v7_0}[revision]
|
||||||
|
connectors = {"7.0": _connectors_v7_0}[revision]
|
||||||
|
if board == "i9":
|
||||||
|
assert revision in ["7.2"]
|
||||||
|
self.revision = revision
|
||||||
|
device = {"7.2": "LFE5U-45F-6BG381C"}[revision]
|
||||||
|
io = {"7.2": _io_v7_2}[revision]
|
||||||
|
connectors = {"7.2": _connectors_v7_2}[revision]
|
||||||
|
|
||||||
|
LatticePlatform.__init__(self, device, io, connectors=connectors, toolchain=toolchain)
|
||||||
|
|
||||||
|
def create_programmer(self):
|
||||||
|
return EcpDapProgrammer()
|
||||||
|
|
||||||
|
def do_finalize(self, fragment):
|
||||||
|
LatticePlatform.do_finalize(self, fragment)
|
||||||
|
self.add_period_constraint(self.lookup_request("clk25", loose=True), 1e9/25e6)
|
||||||
|
self.add_period_constraint(self.lookup_request("eth_clocks:rx", 0, loose=True), 1e9/125e6)
|
||||||
|
self.add_period_constraint(self.lookup_request("eth_clocks:rx", 1, loose=True), 1e9/125e6)
|
28
gateware/sampler.py
Normal file
28
gateware/sampler.py
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
from migen import *
|
||||||
|
|
||||||
|
from litex.soc.interconnect.wishbone import *
|
||||||
|
from litex.soc.integration.soc import SoCRegion
|
||||||
|
|
||||||
|
class Sampler(Module):
|
||||||
|
def __init__(self, adc_pins):
|
||||||
|
# TODO correct addr width
|
||||||
|
self.bus = Interface(data_width=32, adr_width=11)
|
||||||
|
|
||||||
|
# self.clock_domains.foo = ClockDomain() is how to add a new clock domain, accessible at self.foo
|
||||||
|
|
||||||
|
# Provide a slow clock to the ADC, 60MHz / 600 = 100kHz
|
||||||
|
self._counter = Signal(32)
|
||||||
|
self.sync += self._counter.eq(self._counter + 1)
|
||||||
|
self.sync += If(self._counter >= 600, self._counter.eq(0), adc_pins.refclk.eq(~adc_pins.refclk))
|
||||||
|
|
||||||
|
# Set config pins to constant values
|
||||||
|
self.comb += adc_pins.oen_b.eq(0) # Data pins enable
|
||||||
|
self.comb += adc_pins.standby.eq(0) # Sampling standby
|
||||||
|
self.comb += adc_pins.dfs.eq(0) # DFS (raw or two's complement)
|
||||||
|
# The only remaining pin, OTR, is an out of range status indicator
|
||||||
|
|
||||||
|
# Read directly from the data pins into the wishbone bus for now, just for bringup
|
||||||
|
self.comb += self.bus.dat_r.eq(adc_pins.data)
|
||||||
|
self.sync += self.bus.ack.eq(0)
|
||||||
|
self.sync += If(self.bus.cyc & self.bus.stb, self.bus.ack.eq(1))
|
||||||
|
|
37
gateware/timer.py
Normal file
37
gateware/timer.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
from amaranth import *
|
||||||
|
from amaranth_soc.wishbone import *
|
||||||
|
from amaranth_soc.memory import *
|
||||||
|
from math import ceil, log2
|
||||||
|
|
||||||
|
class TimerPeripheral(Elaboratable, Interface):
|
||||||
|
def __init__(self, clock_freq: int, wanted_freq: int):
|
||||||
|
Interface.__init__(self, addr_width=1, data_width=32, granularity=8)
|
||||||
|
memory_map = MemoryMap(addr_width=3, data_width=8)
|
||||||
|
self.memory_map = memory_map
|
||||||
|
|
||||||
|
self.ratio = ceil(clock_freq / wanted_freq)
|
||||||
|
|
||||||
|
def elaborate(self, platform):
|
||||||
|
m = Module()
|
||||||
|
|
||||||
|
counter = Signal(ceil(log2(self.ratio)))
|
||||||
|
value = Signal(32)
|
||||||
|
|
||||||
|
# Up count
|
||||||
|
m.d.sync += counter.eq(counter + 1)
|
||||||
|
|
||||||
|
# Divider value reached, increment
|
||||||
|
with m.If(counter >= self.ratio):
|
||||||
|
m.d.sync += [
|
||||||
|
value.eq(value + 1),
|
||||||
|
counter.eq(0),
|
||||||
|
]
|
||||||
|
|
||||||
|
m.d.sync += self.ack.eq(0)
|
||||||
|
with m.If(self.cyc & self.stb):
|
||||||
|
m.d.sync += [
|
||||||
|
self.ack.eq(1),
|
||||||
|
self.dat_r.eq(value),
|
||||||
|
]
|
||||||
|
|
||||||
|
return m
|
@ -15,3 +15,6 @@ Designed for JLC7628 stackup.
|
|||||||
- I2C should have DNP pullup resistor footprints
|
- I2C should have DNP pullup resistor footprints
|
||||||
- Reset and/or power button would be nice
|
- Reset and/or power button would be nice
|
||||||
- Pads on DDR connector could be thinned slightly
|
- Pads on DDR connector could be thinned slightly
|
||||||
|
- VREF is floating on ADCs
|
||||||
|
- led is on same FPGA pin as ADC1 refclk (U16)
|
||||||
|
- Need to figure out the pin length for mounting the board directly to preprocessor
|
||||||
|
Loading…
Reference in New Issue
Block a user