diff --git a/README.md b/README.md index 1a25e0a..01c16ab 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,32 @@ -# `Colorlight 5A-75X Litex Ethernet Demo` +# `Rust SoC Playground using LiteX, VexRiscV and the Colorlight 5A-75B board` -> Litex SoC and Rust software for [Colorlight 5A-75X][colorlight] board as a simple Ethernet demo. +This repository contains a lot of fun with rust and RiscV. -This project is developed and maintained by [DerFetzer][team]. +The main branch is a small demo of a TCP connection with different options like turning LEDs on/off or registering an external button interrupt. +It is somewhat ceaned up and meant for people who want to get started. -## Usage +Most of the instructions are adapted from https://github.com/DerFetzer/colorlight-litex and https://github.com/icebreaker-fpga/icebreaker-litex-examples: 1. Install Litex as described [here][litex]. 2. Install [ecpprog][ecpprog]. 4. Connect a USB to JTAG adaptor to your board. 5. Build and flash the SOC with something like the following: ``` -python soc/colorlight_5a_75x.py --build --flash --board=5a-75e --revision=6.0 --with-ethernet --sys-clk-freq 50e6 --use-internal-osc +python3 colorlight_5a_75x.py --flash --board=5a-75b --revision=7.0 --with-ethernet --sys-clk-freq 60e6 + ``` -4. Follow the instructions in [eth_demo][eth_demo] to build the software. +4. Follow the instructions in [SoC_demo][SoC_demo] to build the software. + + +There are a few things added to the standard Colorlight 5A-75B. If you just want to try the SoC with one onboard LED, the onboard button and just the TX of the UART you do not need to modify anything. + + +Working in some branch: +- Ethernet with smoltcp thanks to https://github.com/DerFetzer/colorlight-litex +- Interrupts by staring a lot at https://github.com/betrusted-io/xous-core +- A simple second order sigma-delta ADC using a CIC decimator +- An IIR filter using on-chip sigma-delta ADC/DAC +- A peltier temperature controller using a custom on-SoC ratiometric ADC, an IIR filter triggered by timer interrupts, TCP telemetry, UDP temperature set and the onboard line drivers in parallel as peltier drivers ;) ## Credits @@ -26,4 +39,4 @@ and the Litex [target file][target] for the Colorlight board. [litex-example]: https://github.com/icebreaker-fpga/icebreaker-litex-examples [colorlight]: http://www.colorlight-led.com/product/colorlight-5a-75e-led-display-receiving-card.html [target]: https://github.com/litex-hub/litex-boards/blob/master/litex_boards/targets/colorlight_5a_75x.py -[eth_demo]: rust/eth_demo/README.md +[SoC_demo]: rust/SoC_demo/README.md diff --git a/python/recieve.py b/python/recieve.py new file mode 100755 index 0000000..6d4b7a9 --- /dev/null +++ b/python/recieve.py @@ -0,0 +1,48 @@ +import socket +import matplotlib.pyplot as plt +import matplotlib.animation as animation +import datetime as dt + +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +s.connect(('192.168.1.50', 1234)) + +msg = s.recv(4) +s.close() +val = int.from_bytes(msg, byteorder='big', signed=False) +print([x for x in msg]) +print(val) + +fig = plt.figure() +ax = fig.add_subplot(1, 1, 1) +xs = [] +ys = [] + + + +def animate(i, xs, ys): + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.connect(('192.168.1.50', 1234)) + msg = s.recv(4) + s.close() + val = int.from_bytes(msg, byteorder='big', signed=False) + print([x for x in msg]) + print(val) + xs.append(dt.datetime.now().strftime('%H:%M:%S.%f')) + ys.append(val) + # Limit x and y lists to 100 items + xs = xs[-200:] + ys = ys[-200:] + + # Draw x and y lists + ax.clear() + ax.plot(xs, ys) + + # Format plot + plt.xticks(rotation=45, ha='right') + plt.subplots_adjust(bottom=0.30) + plt.title('SoCstream') + plt.ylabel('Temperature') + +# Set up plot to call animate() function periodically +ani = animation.FuncAnimation(fig, animate, fargs=(xs, ys), interval=500) +plt.show() \ No newline at end of file diff --git a/python/recieve_test.py b/python/recieve_test.py new file mode 100755 index 0000000..1ad4c1c --- /dev/null +++ b/python/recieve_test.py @@ -0,0 +1,9 @@ + +import socket + +s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) +s.connect(('192.168.1.50', 1234)) + +msg = s.recv(1024) + +print([x for x in msg]) \ No newline at end of file diff --git a/python/socket b/python/socket new file mode 100644 index 0000000..e69de29 diff --git a/rust/eth_demo/.cargo/config b/rust/SoC_demo/.cargo/config similarity index 100% rename from rust/eth_demo/.cargo/config rename to rust/SoC_demo/.cargo/config diff --git a/rust/eth_demo/.cargo/flash.sh b/rust/SoC_demo/.cargo/flash.sh similarity index 70% rename from rust/eth_demo/.cargo/flash.sh rename to rust/SoC_demo/.cargo/flash.sh index 8743142..5ab4e60 100755 --- a/rust/eth_demo/.cargo/flash.sh +++ b/rust/SoC_demo/.cargo/flash.sh @@ -6,4 +6,4 @@ set -e riscv64-unknown-elf-objcopy $1 -O binary $1.bin # Program Colorlight -ecpprog -o 0x00100000 $1.bin +ecpprog -o 0x00100000 -d i:0x0403:0x6014:1 $1.bin diff --git a/rust/eth_demo/Cargo.lock b/rust/SoC_demo/Cargo.lock similarity index 97% rename from rust/eth_demo/Cargo.lock rename to rust/SoC_demo/Cargo.lock index a02c090..75871bb 100644 --- a/rust/eth_demo/Cargo.lock +++ b/rust/SoC_demo/Cargo.lock @@ -87,9 +87,9 @@ checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" [[package]] name = "byteorder" -version = "1.3.4" +version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cast" @@ -150,18 +150,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "eth_demo" -version = "0.1.0" -dependencies = [ - "litex-pac", - "log", - "managed", - "panic-halt", - "riscv-rt", - "smoltcp", -] - [[package]] name = "gimli" version = "0.22.0" @@ -491,6 +479,20 @@ dependencies = [ "unicode-xid 0.2.1", ] +[[package]] +name = "temp_controller" +version = "0.1.0" +dependencies = [ + "litex-pac", + "log", + "managed", + "panic-halt", + "riscv", + "riscv-rt", + "smoltcp", + "vexriscv", +] + [[package]] name = "termcolor" version = "1.1.0" @@ -574,6 +576,12 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +[[package]] +name = "vexriscv" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eac3d398d0468c76272ea6b2c187d3696f0ed126325f8f41ccc711e9e74cd06" + [[package]] name = "winapi" version = "0.3.9" diff --git a/rust/eth_demo/Cargo.toml b/rust/SoC_demo/Cargo.toml similarity index 87% rename from rust/eth_demo/Cargo.toml rename to rust/SoC_demo/Cargo.toml index 1988207..f5ac603 100644 --- a/rust/eth_demo/Cargo.toml +++ b/rust/SoC_demo/Cargo.toml @@ -1,7 +1,7 @@ [package] -name = "eth_demo" +name = "temp_controller" version = "0.1.0" -authors = ["Piotr Esden-Tempski ", "DerFetzer "] +authors = ["Piotr Esden-Tempski ", "DerFetzer ", "SingularitySurfer"] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -9,6 +9,8 @@ edition = "2018" [dependencies] litex-pac = { path = "../litex-pac" } riscv-rt = "0.8.0" +riscv = "0.6.0" +vexriscv = "0.0.3" panic-halt = "0.2" log = "0.4.11" smoltcp = { version = "0.6", default-features = false, features = ["ethernet", "proto-ipv4", "socket-udp" ,"socket-tcp", "log"] } @@ -22,4 +24,3 @@ debug = true # Improve code generation lto = true codegen-units = 1 - diff --git a/rust/eth_demo/README.md b/rust/SoC_demo/README.md similarity index 65% rename from rust/eth_demo/README.md rename to rust/SoC_demo/README.md index d7c5afc..5d699c3 100644 --- a/rust/eth_demo/README.md +++ b/rust/SoC_demo/README.md @@ -32,28 +32,22 @@ cargo run --release It might be necessary to reset the board with a power cycle or by loading the SOC bitstream again. -You now should have a TCP endpoint at `192.168.1.50:1234` and a UDP endpoint at `192.168.1.50:5678` listening for connections. +You now should have a TCP endpoint at `192.168.1.50:1234`. Connect your computer to the board's Ethernet port an assign a static IP from the `192.168.1.0/24` subnet. -You can now connect to the sockets and get a `Hello World!` back. +You can now connect to the SoC using netcat: ``` -echo "" | netcat 192.168.1.50 1234 -echo "" | netcat -u 192.168.1.50 5678 -``` +rlwrap nc -vv 192.168.1.50 1234 -Connect a USB to UART adapter as described in [litex-boards][uart]. +``` -You should be able to access the console output by running the wishbone-tool. +Now you should have a TCP connection with the SoC and you can talk to it. For example just type "hi" and press enter. Or "led on". If you press the button on the board, you should get a "button pressed" message via TCP. Have a look at main.rs to see which commands the SoC can parse. -``` -wishbone-tool --uart /dev/ttyUSB1 -s terminal -``` -You can find the wishbone-tool here: https://github.com/litex-hub/wishbone-utils -NOTE: If you decided to build your SOC without the `--debug` parameter you can access the console output directly. For example using screen: +NOTE: You can also look at the UART output. Just connect a UART probe to the correct pins (look at the custom pinout at the top of the LiteX SoC description). Example using screen: ``` screen /dev/ttyUSB1 115200 @@ -63,4 +57,3 @@ To exit screen you can type `Ctrl-a k` or `Ctrl-a Ctrl-k` [bug]: https://github.com/rust-lang/cargo/issues/7915#issuecomment-683294870 -[uart]: https://github.com/litex-hub/litex-boards/blob/e4cdbe0f7ad0653e825556d992d233a1723273e3/litex_boards/targets/colorlight_5a_75x.py#L11 diff --git a/rust/SoC_demo/rust-project.json b/rust/SoC_demo/rust-project.json new file mode 100644 index 0000000..8bdd868 --- /dev/null +++ b/rust/SoC_demo/rust-project.json @@ -0,0 +1,4 @@ +{ + "rust.target": "riscv32imac-unknown-none-elf", + "rust.all_targets": false +} diff --git a/rust/SoC_demo/src/adc.rs b/rust/SoC_demo/src/adc.rs new file mode 100644 index 0000000..ba4cbc8 --- /dev/null +++ b/rust/SoC_demo/src/adc.rs @@ -0,0 +1,16 @@ +use litex_pac::ADC; + +pub struct Adc { + registers: ADC, +} + +#[allow(dead_code)] +impl Adc { + pub fn new(registers: ADC) -> Self { + Self { registers } + } + + pub fn read(&mut self) -> u32 { + self.registers.adc_value.read().bits() + } +} diff --git a/rust/SoC_demo/src/dac.rs b/rust/SoC_demo/src/dac.rs new file mode 100644 index 0000000..f900898 --- /dev/null +++ b/rust/SoC_demo/src/dac.rs @@ -0,0 +1,18 @@ +use litex_pac::DAC; + +pub struct Dac { + registers: DAC, +} + +#[allow(dead_code)] +impl Dac { + pub fn new(registers: DAC) -> Self { + Self { registers } + } + + pub fn set(&mut self, value: u32) { + unsafe{ + self.registers.val.write(|w| w.bits(value)); + } + } +} diff --git a/rust/eth_demo/src/ethernet.rs b/rust/SoC_demo/src/ethernet.rs similarity index 100% rename from rust/eth_demo/src/ethernet.rs rename to rust/SoC_demo/src/ethernet.rs diff --git a/rust/SoC_demo/src/gpio.rs b/rust/SoC_demo/src/gpio.rs new file mode 100644 index 0000000..8fcd819 --- /dev/null +++ b/rust/SoC_demo/src/gpio.rs @@ -0,0 +1,103 @@ +use litex_pac::GPIO; + +pub struct Gpio { + registers: GPIO, +} + +#[allow(dead_code)] +impl Gpio { + pub fn new(registers: GPIO) -> Self { + Self { registers } + } + + pub fn status(&mut self) -> u32 { + self.registers.in_.read().bits() + } + + pub fn set_interrupt_polarity(&mut self, pol: bool) { + unsafe { + if pol { + self.registers.polarity.write(|w| w.bits(1)); + } else { + self.registers.polarity.write(|w| w.bits(0)); + } + } + } + + pub fn en_interrupt(&mut self) { + unsafe { + self.registers.ev_enable.write(|w| w.bits(0xFFFF_FFFF)); + } + } + + pub fn clr_interrupt(&mut self) { + unsafe { + self.registers.ev_pending.write(|w| w.bits(0xFFFF_FFFF)); + } + } + + pub fn dis_interrupt(&mut self) { + unsafe { + self.registers.ev_enable.write(|w| w.bits(0)); + } + } + +} + +// pub struct Gpio2 { +// registers: GPIO2, +// } +// +// #[allow(dead_code)] +// impl Gpio2 { +// +// pub fn new(registers: GPIO2) -> Self { +// Self { registers } +// } +// +// pub fn status(&mut self) -> u32 { +// self.registers.in_.read().bits() +// } +// +// pub fn set_interrupt_polarity(&mut self, pol: bool) { +// unsafe { +// if pol { +// self.registers.polarity.write(|w| w.bits(1)); +// }else { +// self.registers.polarity.write(|w| w.bits(0)); +// } +// } +// } +// +// pub fn en_interrupt(&mut self) { +// unsafe { +// +// self.registers.ev_enable.write(|w| w.bits(0xffff)); +// } +// } +// +// pub fn clr_interrupt(&mut self) { +// unsafe { +// self.registers.ev_pending.write(|w| w.bits(0xFFFF_FFFF)); +// } +// } +// +// pub fn dis_interrupt(&mut self) { +// unsafe { +// self.registers.ev_enable.write(|w| w.bits(0)); +// } +// } +// +// pub fn ev_pending(&mut self) -> u32 { +// self.registers.ev_pending.read().bits() +// } +// +// pub fn ev_status(&mut self) -> u32 { +// self.registers.ev_status.read().bits() +// } +// +// pub fn ev_enable(&mut self) -> u32 { +// self.registers.ev_enable.read().bits() +// } +// +// } diff --git a/rust/SoC_demo/src/iir.rs b/rust/SoC_demo/src/iir.rs new file mode 100644 index 0000000..d0ef881 --- /dev/null +++ b/rust/SoC_demo/src/iir.rs @@ -0,0 +1,37 @@ + + +// Biquadratic (BiQuad) Infinite Impulse Response (IIR) Filter. + + +/// Generic vector for integer IIR filter. +/// This struct is used to hold the x/y input/output data vector or the b/a coefficient +/// vector. +pub type Vec5 = [i32; 5]; + + +/// Main IIR struct holds coefficient vector and a shift value which defines the fixed point position +pub struct Iir { + pub ba: Vec5, // b and a coeffitients can be changed. [b0,b1,b2,a1,a2] + pub shift: i32, // shift for fixed point pos + pub xy: Vec5, // x and y internal filter states [x0,x1,y0,y1,y2] +} + +impl Iir { + /// Filter tick. Takes a new inout sample and returns a new output sample. + pub fn tick(&mut self, x0: i32) -> i32 { + + // shift in x0 + self.xy.copy_within(0..4, 1); + self.xy[0] = x0; + + let y0 = 1 << ((self.shift) - 1); + let y = &self.xy + .iter() + .zip(&self.ba) + .map(|(xi, ai)| *xi as i64 * *ai as i64) + .fold(y0, |y, xa| y + xa); + + self.xy[2] = (y >> self.shift) as i32; + self.xy[2] + } +} diff --git a/rust/SoC_demo/src/leds.rs b/rust/SoC_demo/src/leds.rs new file mode 100644 index 0000000..df6e647 --- /dev/null +++ b/rust/SoC_demo/src/leds.rs @@ -0,0 +1,97 @@ +use litex_pac::{LEDS, LEDS2}; + +pub struct Leds { + registers: LEDS, +} + +#[allow(dead_code)] +impl Leds { + pub fn new(registers: LEDS) -> Self { + Self { registers } + } + + pub fn set_single(&mut self, red: bool, yellow: bool, green: bool) { + self.registers.out.write(|w| { + w.r().bit(red); + w.g().bit(green); + w.y().bit(yellow) + }); + } + + pub fn set(&mut self, leds: u32) { + unsafe { + self.registers.out.write(|w| w.bits(leds)); + } + } + + pub fn off(&mut self) { + unsafe { + self.registers.out.write(|w| w.bits(0x0000_0000)); + } + } + + pub fn on(&mut self) { + unsafe { + self.registers.out.write(|w| w.bits(0xFFFF_FFFF)); + } + } + + pub fn toggle(&mut self) { + self.toggle_mask(0xFFFF_FFFF); + } + + pub fn toggle_mask(&mut self, mask: u32) { + let val: u32 = self.registers.out.read().bits() ^ mask; + unsafe { + self.registers.out.write(|w| w.bits(val)); + } + } +} + +pub struct Leds2 { + registers: LEDS2, +} + +#[allow(dead_code)] +impl Leds2 { + pub fn new(registers: LEDS2) -> Self { + Self { registers } + } + + pub fn set_single(&mut self, red: bool, yellow: bool, green: bool) { + self.registers.out.write(|w| { + w.r1().bit(red); + w.g1().bit(green); + w.y1().bit(yellow) + }); + } + + pub fn set(&mut self, leds: u32) { + unsafe { + self.registers.out.write(|w| w.bits(leds)); + } + } + + pub fn off(&mut self) { + unsafe { + self.registers.out.write(|w| w.bits(0x0000_0000)); + } + } + + pub fn on(&mut self) { + unsafe { + self.registers.out.write(|w| w.bits(0xFFFF_FFFF)); + } + } + + pub fn toggle(&mut self) { + self.toggle_mask(0xFFFF_FFFF); + } + + pub fn toggle_mask(&mut self, mask: u32) { + let val: u32 = self.registers.out.read().bits() ^ mask; + unsafe { + self.registers.out.write(|w| w.bits(val)); + } + } +} diff --git a/rust/eth_demo/src/main.rs b/rust/SoC_demo/src/main.rs similarity index 55% rename from rust/eth_demo/src/main.rs rename to rust/SoC_demo/src/main.rs index 876ec3c..6e48766 100644 --- a/rust/eth_demo/src/main.rs +++ b/rust/SoC_demo/src/main.rs @@ -1,17 +1,31 @@ #![no_std] #![no_main] - extern crate panic_halt; +extern crate riscv; +extern crate riscv_rt; use litex_pac; use riscv_rt::entry; +use riscv::register::{mcause, mie, mstatus}; +use vexriscv::register::{vdci, vmim, vmip, vsim, vsip}; + +use riscv::interrupt::{self}; +use core::cell::Cell; + mod ethernet; +mod gpio; +mod leds; mod print; +mod pwm; mod timer; use crate::ethernet::Eth; -use timer::Timer; +use gpio::Gpio; +use leds::{Leds, Leds2}; +use pwm::Pwm; +use timer::{Timer, Timer2}; + use managed::ManagedSlice; use smoltcp::iface::{EthernetInterfaceBuilder, NeighborCache}; @@ -24,7 +38,11 @@ use smoltcp::wire::{EthernetAddress, IpAddress, IpCidr}; use crate::print::UartLogger; use log::{debug, info, trace}; -const SYSTEM_CLOCK_FREQUENCY: u32 = 49_600_000; +const SYSTEM_CLOCK_FREQUENCY: u32 = 60_000_000; + +static mut interrupted:bool = false; +// state of the button interrupt. static mut allows (unsafe) communication between main and irq. + mod mock { use core::cell::Cell; @@ -48,6 +66,8 @@ mod mock { } } + + // This is the entry point for the application. // It is not allowed to return. #[entry] @@ -61,6 +81,14 @@ fn main() -> ! { info!("Logger initialized"); let mut timer = Timer::new(peripherals.TIMER0); + let mut timer2 = Timer2::new(peripherals.TIMER2); + + let mut leds = Leds::new(peripherals.LEDS); + let mut leds2 = Leds2::new(peripherals.LEDS2); + + let mut gpio = Gpio::new(peripherals.GPIO); + + let mut pwm = Pwm::new(peripherals.PWM); let clock = mock::Clock::new(); let device = Eth::new(peripherals.ETHMAC, peripherals.ETHMEM); @@ -77,6 +105,8 @@ fn main() -> ! { .ip_addrs(ip_addrs) .finalize(); + let mut data = [0;128]; + let tcp_server_socket = { // It is not strictly necessary to use a `static mut` and unsafe code here, but // on embedded systems that smoltcp targets it is far better to allocate the data @@ -122,8 +152,28 @@ fn main() -> ! { let tcp_server_handle = socket_set.add(tcp_server_socket); let udp_server_handle = socket_set.add(udp_server_socket); + timer2.load(0); + timer2.reload(SYSTEM_CLOCK_FREQUENCY / 2); + timer2.enable(); + timer2.en_interrupt(); + + pwm.set_period(1 << 15); + pwm.set_value(1 << 13); + + gpio.set_interrupt_polarity(true); + gpio.en_interrupt(); + + + unsafe { + vmim::write(0xFFFF_FFFF); // 1010 for timer and gpio + mstatus::set_mie(); + mie::set_mext(); + } + info!("Main loop..."); + + loop { match iface.poll(&mut socket_set, clock.elapsed()) { Ok(_) => {} @@ -134,16 +184,53 @@ fn main() -> ! { { let mut socket = socket_set.get::(tcp_server_handle); + let mut yay = false; if !socket.is_active() && !socket.is_listening() { info!("Start listen..."); socket.listen(1234).unwrap(); } + else if socket.can_send() && socket.can_recv() { + let bytes_rcvd = socket.recv_slice(&mut data); + info!("bytes rcvd: {:?}", bytes_rcvd.unwrap()); + socket.send_slice(&data).unwrap(); + + if(&data[0..3] == "hi\n".as_bytes()) {socket.send_slice(b"Horrayy! :)\r\n").unwrap();} + + else if(&data[0..7] == "led on\n".as_bytes()) { + leds.on(); + info!("turned led on"); + } + else if(&data[0..8] == "led off\n".as_bytes()) { + leds.off(); + info!("turned led off"); + } + else if(&data[0..15] == "tell me a joke\n".as_bytes()) { + socket.send_slice(b"Was machen die Pilze auf der Pizza? Als Belag funghieren. Ha.\r\n").unwrap(); + } + else if(&data[0..7] == "set pwm".as_bytes()) { + let value = as_u32_be(&data[9..13]); + pwm.set_value(value); + socket.send_slice(b"set pwm value.\r\n").unwrap(); + info!("set pwm value to {}", value); + } + else { + socket.send_slice(b"I didn't understand, sorry...\r\n").unwrap(); + info!("wtf is this other computer talking about??"); + } + + for elem in data.iter_mut() { *elem = 0; } + } + if socket.can_send() { - info!("Can send..."); - socket.send_slice(b"Hello World!\r\n").unwrap(); - socket.close(); + unsafe { + if interrupted { + socket.send_slice(b"Someone pressed my Button!\r\n").unwrap(); + info!("interrupt noticed"); + interrupted=false; + } + } } } @@ -153,18 +240,12 @@ fn main() -> ! { socket.bind(5678).unwrap() } - let client = match socket.recv() { + match socket.recv() { Ok((data, endpoint)) => { - debug!("udp:5678 recv data: {:?} from {}", data, endpoint); - Some(endpoint) + info!("Hello World"); } - Err(_) => None, + Err(_) => debug!("Err"), }; - if let Some(endpoint) = client { - let data = b"Hello World!\r\n"; - debug!("udp:5678 send data: {:?}", data); - socket.send_slice(data, endpoint).unwrap(); - } } match iface.poll_delay(&socket_set, clock.elapsed()) { @@ -180,6 +261,48 @@ fn main() -> ! { } } +#[no_mangle] +fn MachineExternal() { + let mc = mcause::read(); + let irqs_pending = vmip::read(); + if mc.is_exception() {}; + for irq_no in 0..32 { + if irqs_pending & (1 << irq_no) != 0 { + match isr_to_interrupt(irq_no) { + Some(0) => system_tick(), + Some(1) => button_irq(), + _ => unsafe{vmim::write(vmim::read() ^ (1< Option { + if isr == (litex_pac::Interrupt::TIMER2 as u8) {return Some(0)} + if isr == (litex_pac::Interrupt::GPIO as u8) {return Some(1)} + + None +} + + +// Main signal processing routine. Triggered by Timer2. +fn system_tick() { + let peripherals = unsafe { litex_pac::Peripherals::steal() }; // steal all but only use the safe ones ;) + let mut timer2 = Timer2::new(peripherals.TIMER2); + let mut leds2 = Leds2::new(peripherals.LEDS2); + leds2.toggle(); + timer2.clr_interrupt(); +} + +fn button_irq() { + let peripherals = unsafe { litex_pac::Peripherals::steal() }; // steal all but only use the safe ones ;) + + let mut gpio = Gpio::new(peripherals.GPIO); + gpio.clr_interrupt(); + unsafe {interrupted = true}; +} + + fn msleep(timer: &mut Timer, ms: u32) { timer.disable(); @@ -189,6 +312,22 @@ fn msleep(timer: &mut Timer, ms: u32) { timer.enable(); // Wait until the time has elapsed - while timer.value() > 0 {} + while timer.value() > 0 { + } timer.disable(); } + + +fn as_u32_be(array: &[u8]) -> u32 { + ((array[0] as u32) << 24) + + ((array[1] as u32) << 16) + + ((array[2] as u32) << 8) + + ((array[3] as u32) << 0) +} + +fn as_u32_le(array: &[u8]) -> u32 { + ((array[0] as u32) << 0) + + ((array[1] as u32) << 8) + + ((array[2] as u32) << 16) + + ((array[3] as u32) << 24) +} diff --git a/rust/eth_demo/src/print.rs b/rust/SoC_demo/src/print.rs similarity index 100% rename from rust/eth_demo/src/print.rs rename to rust/SoC_demo/src/print.rs diff --git a/rust/SoC_demo/src/pwm.rs b/rust/SoC_demo/src/pwm.rs new file mode 100644 index 0000000..780b63c --- /dev/null +++ b/rust/SoC_demo/src/pwm.rs @@ -0,0 +1,23 @@ +use litex_pac::PWM; + +pub struct Pwm { + registers: PWM, +} + +impl Pwm { + pub fn new(registers: PWM) -> Self { + Self { registers } + } + + pub fn set_period(&mut self, period: u32) { + unsafe { + self.registers.period.write(|w| w.bits(period)); + } + } + + pub fn set_value(&mut self, val: u32) { + unsafe { + self.registers.value.write(|w| w.bits(val)); + } + } +} diff --git a/rust/SoC_demo/src/timer.rs b/rust/SoC_demo/src/timer.rs new file mode 100644 index 0000000..e15ebb2 --- /dev/null +++ b/rust/SoC_demo/src/timer.rs @@ -0,0 +1,145 @@ +use litex_pac::{TIMER0, TIMER2}; + +pub struct Timer { + registers: TIMER0, +} + +impl Timer { + pub fn new(registers: TIMER0) -> Self { + Self { registers } + } + + pub fn enable(&mut self) { + unsafe { + self.registers.en.write(|w| w.bits(1)); + } + } + + pub fn disable(&mut self) { + unsafe { + self.registers.en.write(|w| w.bits(0)); + } + } + + pub fn load(&mut self, value: u32) { + unsafe { + self.registers.load.write(|w| w.bits(value)); + } + } + + pub fn reload(&mut self, value: u32) { + unsafe { + self.registers.reload.write(|w| w.bits(value)); + } + } + + pub fn value(&mut self) -> u32 { + unsafe { + self.registers.update_value.write(|w| w.bits(1)); + } + + self.registers.value.read().bits() + } + + pub fn en_interrupt(&mut self) { + unsafe { + self.registers.ev_enable.write(|w| w.bits(0x1)); + } + } + + pub fn clr_interrupt(&mut self) { + unsafe { + self.registers.ev_pending.write(|w| w.bits(0xFFFF_FFFF)); + } + } + + pub fn dis_interrupt(&mut self) { + unsafe { + self.registers.ev_enable.write(|w| w.bits(0)); + } + } + + pub fn ev_pending(&mut self) -> u32 { + self.registers.ev_pending.read().bits() + } + + pub fn ev_status(&mut self) -> u32 { + self.registers.ev_status.read().bits() + } + + pub fn ev_enable(&mut self) -> u32 { + self.registers.ev_enable.read().bits() + } +} + +pub struct Timer2 { + registers: TIMER2, +} + +impl Timer2 { + pub fn new(registers: TIMER2) -> Self { + Self { registers } + } + + pub fn enable(&mut self) { + unsafe { + self.registers.en.write(|w| w.bits(1)); + } + } + + pub fn disable(&mut self) { + unsafe { + self.registers.en.write(|w| w.bits(0)); + } + } + + pub fn load(&mut self, value: u32) { + unsafe { + self.registers.load.write(|w| w.bits(value)); + } + } + + pub fn reload(&mut self, value: u32) { + unsafe { + self.registers.reload.write(|w| w.bits(value)); + } + } + + pub fn value(&mut self) -> u32 { + unsafe { + self.registers.update_value.write(|w| w.bits(1)); + } + + self.registers.value.read().bits() + } + + pub fn en_interrupt(&mut self) { + unsafe { + self.registers.ev_enable.write(|w| w.bits(0x1)); + } + } + + pub fn clr_interrupt(&mut self) { + unsafe { + self.registers.ev_pending.write(|w| w.bits(0xFFFF_FFFF)); + } + } + + pub fn dis_interrupt(&mut self) { + unsafe { + self.registers.ev_enable.write(|w| w.bits(0)); + } + } + + pub fn ev_pending(&mut self) -> u32 { + self.registers.ev_pending.read().bits() + } + + pub fn ev_status(&mut self) -> u32 { + self.registers.ev_status.read().bits() + } + + pub fn ev_enable(&mut self) -> u32 { + self.registers.ev_enable.read().bits() + } +} diff --git a/rust/eth_demo/src/timer.rs b/rust/eth_demo/src/timer.rs deleted file mode 100644 index 130d887..0000000 --- a/rust/eth_demo/src/timer.rs +++ /dev/null @@ -1,43 +0,0 @@ -use litex_pac::TIMER0; - -pub struct Timer { - registers: TIMER0, -} - -impl Timer { - pub fn new(registers: TIMER0) -> Self { - Self { registers } - } - - pub fn enable(&mut self) { - unsafe { - self.registers.en.write(|w| w.bits(1)); - } - } - - pub fn disable(&mut self) { - unsafe { - self.registers.en.write(|w| w.bits(0)); - } - } - - pub fn load(&mut self, value: u32) { - unsafe { - self.registers.load.write(|w| w.bits(value)); - } - } - - pub fn reload(&mut self, value: u32) { - unsafe { - self.registers.reload.write(|w| w.bits(value)); - } - } - - pub fn value(&mut self) -> u32 { - unsafe { - self.registers.update_value.write(|w| w.bits(1)); - } - - self.registers.value.read().bits() - } -} diff --git a/rust/litex-pac/clSOC.svd b/rust/litex-pac/clSOC.svd index 1596c26..963a81e 100644 --- a/rust/litex-pac/clSOC.svd +++ b/rust/litex-pac/clSOC.svd @@ -2,7 +2,7 @@ litex - ETHMAC + SOC 8 32 @@ -14,16 +14,15 @@ CTRL - 0xE0000000 + 0xF0000000 CTRL RESET - + 0x0000 0x00 32 - read-write reset @@ -41,7 +40,6 @@ to the Wishbone/CSR bus are working correctly. The initial reset value of 0x0004 0x12345678 32 - read-write scratch @@ -57,7 +55,6 @@ to the Wishbone/CSR bus are working correctly. The initial reset value of 0x0008 0x00 32 - read-only bus_errors @@ -76,7 +73,7 @@ to the Wishbone/CSR bus are working correctly. The initial reset value of IDENTIFIER_MEM - 0xE0001000 + 0xF0001000 IDENTIFIER_MEM @@ -85,7 +82,6 @@ to the Wishbone/CSR bus are working correctly. The initial reset value of 0x0000 0x00 32 - read-write identifier_mem @@ -102,36 +98,9 @@ to the Wishbone/CSR bus are working correctly. The initial reset value of registers - - UART_PHY - 0xE0001800 - UART_PHY - - - TUNING_WORD - 0x0000 - 0x96feb4 - 32 - read-write - - - tuning_word - 31 - [31:0] - 0 - - - - - - 0 - 0x4 - registers - - UART - 0xE0002000 + 0xF0002000 UART @@ -139,7 +108,6 @@ to the Wishbone/CSR bus are working correctly. The initial reset value of 0x0000 0x00 32 - read-write rxtx @@ -154,7 +122,6 @@ to the Wishbone/CSR bus are working correctly. The initial reset value of 0x0004 0x00 32 - read-only txfull @@ -169,7 +136,6 @@ to the Wishbone/CSR bus are working correctly. The initial reset value of 0x0008 0x00 32 - read-only rxempty @@ -181,46 +147,73 @@ to the Wishbone/CSR bus are working correctly. The initial reset value of EV_STATUS + 0x000c 0x00 32 - read-write - status - 1 - [1:0] + tx + 0 + [0:0] 0 + + + + rx + 1 + [1:1] + 1 + EV_PENDING + 0x0010 0x00 32 - read-write - pending - 1 - [1:0] + tx + 0 + [0:0] 0 + + + + rx + 1 + [1:1] + 1 + EV_ENABLE + 0x0014 0x00 32 - read-write - enable - 1 - [1:0] + tx + 0 + [0:0] 0 + + + + rx + 1 + [1:1] + 1 + @@ -229,7 +222,6 @@ to the Wishbone/CSR bus are working correctly. The initial reset value of 0x0018 0x00 32 - read-only txempty @@ -244,7 +236,6 @@ to the Wishbone/CSR bus are working correctly. The initial reset value of 0x001c 0x00 32 - read-only rxfull @@ -267,7 +258,7 @@ to the Wishbone/CSR bus are working correctly. The initial reset value of TIMER0 - 0xE0002800 + 0xF0002800 TIMER0 @@ -277,7 +268,6 @@ this register specifies the Timer's duration in clock cycles.]]> 0x0000 0x00 32 - read-write load @@ -294,7 +284,6 @@ this register specify the Timer's period in clock cycles.]]> 0x0004 0x00 32 - read-write reload @@ -311,7 +300,6 @@ to ``0`` to disable the Timer.]]> 0x0008 0x00 32 - read-write en @@ -328,7 +316,6 @@ the current countdown value to ``value`` register.]]> 0x000c 0x00 32 - read-write update_value @@ -344,7 +331,6 @@ the current countdown value to ``value`` register.]]> 0x0010 0x00 32 - read-only value @@ -356,46 +342,52 @@ the current countdown value to ``value`` register.]]> EV_STATUS + 0x0014 0x00 32 - read-write - status + zero 0 [0:0] 0 + EV_PENDING + 0x0018 0x00 32 - read-write - pending + zero 0 [0:0] 0 + EV_ENABLE + 0x001c 0x00 32 - read-write - enable + zero 0 [0:0] 0 + @@ -412,7 +404,7 @@ the current countdown value to ``value`` register.]]> SDRAM - 0xE0003000 + 0xF0003000 SDRAM @@ -420,7 +412,6 @@ the current countdown value to ``value`` register.]]> 0x0000 0x01 32 - read-write sel @@ -457,7 +448,6 @@ the current countdown value to ``value`` register.]]> 0x0004 0x00 32 - read-write dfii_pi0_command @@ -472,7 +462,6 @@ the current countdown value to ``value`` register.]]> 0x0008 0x00 32 - read-write dfii_pi0_command_issue @@ -487,7 +476,6 @@ the current countdown value to ``value`` register.]]> 0x000c 0x00 32 - read-write dfii_pi0_address @@ -502,12 +490,11 @@ the current countdown value to ``value`` register.]]> 0x0010 0x00 32 - read-write dfii_pi0_baddress - 1 - [1:0] + 0 + [0:0] 0 @@ -517,7 +504,6 @@ the current countdown value to ``value`` register.]]> 0x0014 0x00 32 - read-write dfii_pi0_wrdata @@ -532,7 +518,6 @@ the current countdown value to ``value`` register.]]> 0x0018 0x00 32 - read-only dfii_pi0_rddata @@ -542,106 +527,16 @@ the current countdown value to ``value`` register.]]> - - DFII_PI1_COMMAND - 0x001c - 0x00 - 32 - read-write - - - dfii_pi1_command - 5 - [5:0] - 0 - - - - - DFII_PI1_COMMAND_ISSUE - 0x0020 - 0x00 - 32 - read-write - - - dfii_pi1_command_issue - 0 - [0:0] - 0 - - - - - DFII_PI1_ADDRESS - 0x0024 - 0x00 - 32 - read-write - - - dfii_pi1_address - 10 - [10:0] - 0 - - - - - DFII_PI1_BADDRESS - 0x0028 - 0x00 - 32 - read-write - - - dfii_pi1_baddress - 1 - [1:0] - 0 - - - - - DFII_PI1_WRDATA - 0x002c - 0x00 - 32 - read-write - - - dfii_pi1_wrdata - 31 - [31:0] - 0 - - - - - DFII_PI1_RDDATA - 0x0030 - 0x00 - 32 - read-only - - - dfii_pi1_rddata - 31 - [31:0] - 0 - - - 0 - 0x34 + 0x1c registers SPIFLASH - 0xE0003800 + 0xF0003800 SPIFLASH @@ -650,7 +545,6 @@ the current countdown value to ``value`` register.]]> 0x0000 0x00 32 - read-write mosi @@ -688,7 +582,6 @@ the current countdown value to ``value`` register.]]> 0x0004 0x00 32 - read-only miso @@ -704,7 +597,6 @@ the current countdown value to ``value`` register.]]> 0x0008 0x00 32 - read-write bitbang_en @@ -723,7 +615,7 @@ the current countdown value to ``value`` register.]]> ETHPHY - 0xE0004000 + 0xF0004000 ETHPHY @@ -731,7 +623,6 @@ the current countdown value to ``value`` register.]]> 0x0000 0x00 32 - read-write crg_reset @@ -746,7 +637,6 @@ the current countdown value to ``value`` register.]]> 0x0004 0x00 32 - read-only link_status @@ -776,7 +666,6 @@ the current countdown value to ``value`` register.]]> 0x0008 0x00 32 - read-write mdc @@ -806,7 +695,6 @@ the current countdown value to ``value`` register.]]> 0x000c 0x00 32 - read-only r @@ -826,7 +714,7 @@ the current countdown value to ``value`` register.]]> ETHMAC - 0xE0004800 + 0xF0004800 ETHMAC @@ -834,7 +722,6 @@ the current countdown value to ``value`` register.]]> 0x0000 0x00 32 - read-only sram_writer_slot @@ -849,7 +736,6 @@ the current countdown value to ``value`` register.]]> 0x0004 0x00 32 - read-only sram_writer_length @@ -864,7 +750,6 @@ the current countdown value to ``value`` register.]]> 0x0008 0x00 32 - read-only sram_writer_errors @@ -876,46 +761,53 @@ the current countdown value to ``value`` register.]]> SRAM_WRITER_EV_STATUS + 0x000c 0x00 32 - read-write - sram_writer_ev_status + available 0 [0:0] 0 + SRAM_WRITER_EV_PENDING + 0x0010 0x00 32 - read-write - sram_writer_ev_pending + available 0 [0:0] 0 + SRAM_WRITER_EV_ENABLE + 0x0014 0x00 32 - read-write - sram_writer_ev_enable + available 0 [0:0] 0 + @@ -924,7 +816,6 @@ the current countdown value to ``value`` register.]]> 0x0018 0x00 32 - read-write sram_reader_start @@ -939,7 +830,6 @@ the current countdown value to ``value`` register.]]> 0x001c 0x00 32 - read-only sram_reader_ready @@ -954,7 +844,6 @@ the current countdown value to ``value`` register.]]> 0x0020 0x00 32 - read-only sram_reader_level @@ -969,7 +858,6 @@ the current countdown value to ``value`` register.]]> 0x0024 0x00 32 - read-write sram_reader_slot @@ -984,7 +872,6 @@ the current countdown value to ``value`` register.]]> 0x0028 0x00 32 - read-write sram_reader_length @@ -996,46 +883,52 @@ the current countdown value to ``value`` register.]]> SRAM_READER_EV_STATUS + 0x002c 0x00 32 - read-write - sram_reader_ev_status + done 0 [0:0] 0 + SRAM_READER_EV_PENDING + 0x0030 0x00 32 - read-write - sram_reader_ev_pending + done 0 [0:0] 0 + SRAM_READER_EV_ENABLE + 0x0034 0x00 32 - read-write - sram_reader_ev_enable + done 0 [0:0] 0 + @@ -1044,7 +937,6 @@ the current countdown value to ``value`` register.]]> 0x0038 0x01 32 - read-only preamble_crc @@ -1059,7 +951,6 @@ the current countdown value to ``value`` register.]]> 0x003c 0x00 32 - read-only preamble_errors @@ -1074,7 +965,6 @@ the current countdown value to ``value`` register.]]> 0x0040 0x00 32 - read-only crc_errors @@ -1095,6 +985,380 @@ the current countdown value to ``value`` register.]]> 2 + + LEDS + 0xF0005000 + LEDS + + + OUT + 0x0000 + 0x00 + 32 + + + r + 0 + [0:0] + 0 + + + + y + 1 + [1:1] + 1 + + + + g + 2 + [2:2] + 2 + + + + user_led_n + 3 + [3:3] + 3 + + + + + + + 0 + 0x4 + registers + + + + LEDS2 + 0xF0005800 + LEDS2 + + + OUT + 0x0000 + 0x00 + 32 + + + r1 + 0 + [0:0] + 0 + + + + y1 + 1 + [1:1] + 1 + + + + g1 + 2 + [2:2] + 2 + + + + + + + 0 + 0x4 + registers + + + + GPIO + 0xF0006000 + GPIO + + + IN + + 0x0000 + 0x00 + 32 + + + in + 0 + [0:0] + 0 + + + + + POLARITY + + 0x0004 + 0x00 + 32 + + + polarity + 0 + [0:0] + 0 + + + + + EV_STATUS + + 0x0008 + 0x00 + 32 + + + i0 + 0 + [0:0] + 0 + + + + + + EV_PENDING + + 0x000c + 0x00 + 32 + + + i0 + 0 + [0:0] + 0 + + + + + + EV_ENABLE + + 0x0010 + 0x00 + 32 + + + i0 + 0 + [0:0] + 0 + + + + + + + 0 + 0x14 + registers + + + gpio + 3 + + + + TIMER2 + 0xF0006800 + TIMER2 + + + LOAD + + 0x0000 + 0x00 + 32 + + + load + 31 + [31:0] + 0 + + + + + RELOAD + + 0x0004 + 0x00 + 32 + + + reload + 31 + [31:0] + 0 + + + + + EN + + 0x0008 + 0x00 + 32 + + + en + 0 + [0:0] + 0 + + + + + UPDATE_VALUE + + 0x000c + 0x00 + 32 + + + update_value + 0 + [0:0] + 0 + + + + + VALUE + + 0x0010 + 0x00 + 32 + + + value + 31 + [31:0] + 0 + + + + + EV_STATUS + + 0x0014 + 0x00 + 32 + + + zero + 0 + [0:0] + 0 + + + + + + EV_PENDING + + 0x0018 + 0x00 + 32 + + + zero + 0 + [0:0] + 0 + + + + + + EV_ENABLE + + 0x001c + 0x00 + 32 + + + zero + 0 + [0:0] + 0 + + + + + + + 0 + 0x20 + registers + + + timer2 + 4 + + + + PWM + 0xF0007000 + PWM + + + PERIOD + + 0x0000 + 0x00 + 32 + + + period + 31 + [31:0] + 0 + + + + + VALUE + + 0x0004 + 0x00 + 32 + + + value + 31 + [31:0] + 0 + + + + + + 0 + 0x8 + registers + + ETHMEM 0x80000000 @@ -1180,4 +1444,62 @@ the current countdown value to ``value`` register.]]> + + + + SRAM + 0x10000000 + 0x00002000 + + + MAIN_RAM + 0x40000000 + 0x00400000 + + + SPIFLASH + 0x20000000 + 0x02000000 + + + ROM + 0x20100000 + 0x01F00000 + + + ETHMAC + 0x80000000 + 0x00002000 + + + CSR + 0xF0000000 + 0x00010000 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/rust/litex-pac/memory.x b/rust/litex-pac/memory.x index 2ac22b8..7d37e38 100644 --- a/rust/litex-pac/memory.x +++ b/rust/litex-pac/memory.x @@ -1,10 +1,10 @@ MEMORY { sram : ORIGIN = 0x10000000, LENGTH = 0x00002000 - main_ram : ORIGIN = 0x40000000, LENGTH = 0x00800000 + main_ram : ORIGIN = 0x40000000, LENGTH = 0x00400000 spiflash : ORIGIN = 0x20000000, LENGTH = 0x02000000 rom : ORIGIN = 0x20100000, LENGTH = 0x01f00000 ethmac : ORIGIN = 0x80000000, LENGTH = 0x00002000 - csr : ORIGIN = 0xe0000000, LENGTH = 0x00010000 + csr : ORIGIN = 0xf0000000, LENGTH = 0x00010000 } REGION_ALIAS("REGION_TEXT", spiflash); diff --git a/soc/adc.py b/soc/adc.py new file mode 100644 index 0000000..4026930 --- /dev/null +++ b/soc/adc.py @@ -0,0 +1,90 @@ +# SingularitySurfer 2020 + + +import numpy as np +from migen import * +from litex.soc.interconnect.csr import * +from functools import reduce +from operator import and_ + + +class ADC(Module, AutoCSR): + """Basic sigma-delta ADC with a CIC decimator running at sys clock. + The decimator gain is such that the output bitwidth will always be maximized. + """ + def __init__(self, cic_order=6, cic_ratechange=2**7, width_o=32, clk_div=0): + self.inp = Signal() # analog in + self.sd = Signal() # sigma-delta switching pin + self.dout = Signal(width_o) + self.stb = Signal() # data out strobe + ### + + width_csr = 32 + self.adc_val = CSRStatus(width_csr, name="adc_value", description="adc conversion value (continuously updated)") + + + if not(cic_ratechange & (cic_ratechange-1) == 0) and not cic_ratechange != 0: + raise ValueError() # not a power of two + + + if clk_div != 0: + divcnt = Signal(clk_div) + self.sync += divcnt.eq(divcnt+1) + + b_max = np.ceil(np.log2(cic_ratechange)) # max bit growth + + if clk_div != 0: + self.sync += [ + If(reduce(and_, divcnt), # do the sigma-delta + self.sd.eq(self.inp)) + ] + else: + self.sync += [ # do the sigma-delta + self.sd.eq(self.inp) + ] + + width = (int(b_max)*cic_order)+1 + sig = Signal((width, True), reset_less=True) + self.comb += sig.eq(self.sd) + for _ in range(cic_order): # integrator cascade + sum = Signal((width, True), reset_less=True) + self.sync += [ + sum.eq(sum + sig) + ] + sig = sum + + cnt = Signal(int(b_max)) + self.sync += cnt.eq(cnt+1) + for _ in range(cic_order): # comb cascade + dif = Signal((width, True), reset_less=True) + reg = Signal((width, True), reset_less=True) + self.sync += [ + self.stb.eq(0), + If(reduce(and_, cnt), # counter up and therefore new output sample + self.stb.eq(1), + reg.eq(sig), + dif.eq(sig - reg) + ) + ] + sig = dif + + self.comb += self.dout.eq(sig[-width_o:]) # highest bits are the data output + self.sync += self.adc_val.status.eq(sig[-width_csr:]) # continuously update CSR + + + def sim(self): + yield + yield self.inp.eq(1) + for i in range(10000): + yield + if(i==3000): + yield self.inp.eq(~self.inp) + if (i == 5000): + yield self.inp.eq(~self.inp) + if i>7000: + yield self.inp.eq(~self.inp) + + +if __name__ == "__main__": + test = ADC(6,2**8,16) + run_simulation(test, test.sim(), vcd_name="adc.vcd") \ No newline at end of file diff --git a/soc/colorlight_5a_75x.py b/soc/colorlight_5a_75x.py index 0fb031e..fe43c52 100755 --- a/soc/colorlight_5a_75x.py +++ b/soc/colorlight_5a_75x.py @@ -26,6 +26,8 @@ from litex.soc.integration.builder import Builder, builder_argdict, builder_args from litex.soc.integration.soc_core import soc_core_argdict, soc_core_args from litex.soc.interconnect.csr import AutoCSR, CSRStorage, CSRStatus, CSRField +from litex.soc.integration.doc import AutoDoc, ModuleDoc +from litex.soc.cores.timer import Timer from litex.build.generic_platform import * @@ -37,6 +39,12 @@ import litex.soc.doc as lxsocdoc +from adc import ADC +from leds import Leds +from pwm import PWM + +from litex.soc.cores.gpio import GPIOIn + class ECP5Programmer(GenericProgrammer): needs_bitreverse = False @@ -48,6 +56,63 @@ def load_bitstream(self, bitstream_file): subprocess.call(["ecpprog", "-S", bitstream_file]) +# My IOs ------------------------------------------------------------------------------------------- + + +_myserial = [ + ("myserial", 0, + Subsignal("tx", Pins("j1:14")), + Subsignal("rx", Pins("j1:13")), + IOStandard("LVCMOS33") + ) +] + +_dac = [ + ("dac", 0, Pins("j4:0"), IOStandard("LVCMOS33")), + ("dac", 1, Pins("j4:1"), IOStandard("LVCMOS33")), # sigma-delta dac output +] + +_peltier_driver = [ + ("pd", 0, Pins("j3:0"), IOStandard("LVCMOS33")), + ("pd", 1, Pins("j3:1"), IOStandard("LVCMOS33")), + ("pd", 2, Pins("j3:2"), IOStandard("LVCMOS33")), + ("pd", 3, Pins("j3:4"), IOStandard("LVCMOS33")), + ("pd", 4, Pins("j2:2"), IOStandard("LVCMOS33")), + ("pd", 5, Pins("j2:4"), IOStandard("LVCMOS33")), + ("pd", 6, Pins("j2:5"), IOStandard("LVCMOS33")), + ("pd", 7, Pins("j2:6"), IOStandard("LVCMOS33")), +] + +_pwm = [ + ("pwm", 0, Pins("j2:0"), IOStandard("LVCMOS33")), +] + +_leds = [ + ("g", 0, Pins("j6:5"), IOStandard("LVCMOS33")), + ("r", 0, Pins("j6:10"), IOStandard("LVCMOS33")), # Not really here but there is congestion with the pins otherwise.. + ("y", 0, Pins("j6:9"), IOStandard("LVCMOS33")), + ("g1", 0, Pins("j7:1"), IOStandard("LVCMOS33")), + ("r1", 0, Pins("j7:0"), IOStandard("LVCMOS33")), + ("y1", 0, Pins("j7:2"), IOStandard("LVCMOS33")), +] + +_adc_first_order = [ + ("in", 0, Pins("j1:1"), IOStandard("LVDS")), + ("sd", 0, Pins("j1:2"), IOStandard("LVCMOS33")), # sigma delta out + ("p3v", 0, Pins("j1:0"), IOStandard("LVCMOS33")), # this will make 3V on the connector + ("p5v", 0, Pins("j1:14"), IOStandard("LVCMOS33")), # this will make 5V on the connector bc buffer IC +] + +_adc_second_order = [ + ("in", 0, Pins("j1:1"), IOStandard("LVDS")), + ("sd", 0, Pins("j1:5"), IOStandard("LVCMOS33")), # sigma delta out + ("sd", 1, Pins("j1:7"), IOStandard("LVCMOS33")), # sigma delta out (copy of first) + ("p3v", 0, Pins("j1:0"), IOStandard("LVCMOS33")), # this will make 3V on the connector + ("p5v", 0, Pins("j1:14"), IOStandard("LVCMOS33")), # this will make 5V on the connector bc buffer IC +] + + + # CRG ---------------------------------------------------------------------------------------------- class _CRG(Module): @@ -101,6 +166,19 @@ def __init__(self, platform, sys_clk_freq, use_internal_osc=False, with_usb_pll= self.specials += DDROutput(1, 0, platform.request("sdram_clock"), sdram_clk) +class DAC(Module, AutoCSR): + """Basic first order sigma-delta DAC running at sys clock""" + def __init__(self, pin, bits): + self.val = CSRStorage(bits, description='dac output value') + self.pin = pin + accu = Signal(bits+1) + self.sync += [ + accu.eq(accu[:-1] + self.val.storage), # clever form of integrator with feedback + pin.eq(accu[-1]) + ] + + + # BaseSoC ------------------------------------------------------------------------------------------ class BaseSoC(SoCCore): @@ -132,6 +210,9 @@ def __init__(self, debug, flash_offset, board, revision, with_ethernet=False, wi if board == "5a-75e" and revision == "6.0" and (with_etherbone or with_ethernet): assert use_internal_osc, "You cannot use the 25MHz clock as system clock since it is provided by the Ethernet PHY and will stop during PHY reset." + # add custom serial port ios + platform.add_extension(_myserial) + # Set cpu name and variant defaults when none are provided if "cpu_variant" not in kwargs: if debug: @@ -147,6 +228,9 @@ def __init__(self, debug, flash_offset, board, revision, with_ethernet=False, wi # Set CPU reset address kwargs["cpu_reset_address"] = self.mem_map["spiflash"] + flash_offset + # defaul uart myserial + kwargs["uart_name"] = "myserial" + # Select "crossover" as soc uart instead of "serial" # We have to make that selection before calling the parent initializer if debug: @@ -158,7 +242,7 @@ def __init__(self, debug, flash_offset, board, revision, with_ethernet=False, wi ident_version = True, **kwargs) - with_rst = kwargs["uart_name"] not in ["serial", "bridge", "crossover"] # serial_rx shared with user_btn_n. + with_rst = False # kwargs["uart_name"] not in ["serial", "bridge", "crossover"] # serial_rx shared with user_btn_n. with_usb_pll = kwargs.get("uart_name", None) == "usb_acm" self.submodules.crg = _CRG(platform, sys_clk_freq, use_internal_osc=use_internal_osc, with_usb_pll=with_usb_pll, with_rst=with_rst, sdram_rate=sdram_rate) @@ -202,9 +286,15 @@ def __init__(self, debug, flash_offset, board, revision, with_ethernet=False, wi # Ethernet / Etherbone --------------------------------------------------------------------- if with_ethernet or with_etherbone: - self.submodules.ethphy = LiteEthPHYRGMII( - clock_pads = self.platform.request("eth_clocks", eth_phy), - pads = self.platform.request("eth", eth_phy)) + if board == "5a-75b" and revision == "7.0": + self.submodules.ethphy = LiteEthPHYRGMII( + clock_pads = self.platform.request("eth_clocks", eth_phy), + pads = self.platform.request("eth", eth_phy), + tx_delay = 0e-9) + else: + self.submodules.ethphy = LiteEthPHYRGMII( + clock_pads = self.platform.request("eth_clocks", eth_phy), + pads = self.platform.request("eth", eth_phy)) self.add_csr("ethphy") if with_ethernet: self.add_ethernet(phy=self.ethphy) @@ -212,11 +302,75 @@ def __init__(self, debug, flash_offset, board, revision, with_ethernet=False, wi self.add_etherbone(phy=self.ethphy) + # add IO extentions + platform.add_extension(_leds) + platform.add_extension(_pwm) + platform.add_extension(_adc_second_order) + platform.add_extension(_dac) + platform.add_extension(_peltier_driver) + + + # LEDs blinkyblinky :) + + self.submodules.leds = Leds(Cat( + platform.request("r"), + platform.request("y"), + platform.request("g"), + platform.request("user_led_n"), + ), + led_polarity=0x8, + led_name=[ + ["r", "The Red LED."], + ["y", "The Yellow LED."], + ["g", "The Green Red LED."], + ["user_led_n", "The onboard LED."] + ]) + + self.add_csr("leds") + + self.submodules.leds2 = Leds(Cat( + platform.request("r1"), + platform.request("y1"), + platform.request("g1") + ), + led_polarity=0x00, + led_name=[ + ["r1", "The second Red LED."], + ["y1", "The second Yellow LED."], + ["g1", "The second Green Red LED."] + ]) + + self.add_csr("leds2") + + self.submodules.gpio = gpio = GPIOIn(platform.request("user_btn_n", 0), with_irq=True) + self.add_csr("gpio") + self.add_interrupt("gpio") + + self.submodules.timer2 = Timer() + self.add_csr("timer2") + self.add_interrupt("timer2") + + self.submodules.pwm = PWM(platform.request("pwm"), width=32) + self.add_csr("pwm") + + + + + + # Helper functions --------------------------------------------------------------------------------- def modify_svd(builder_kwargs): # Add Ethernet buffer peripheral to svd with open(builder_kwargs["csr_svd"], "r") as f: + line_number = 0 + for l in f: # search for the right place to insert + line_number += 1 + if """""" in l: + line = line_number + print("inserting before line:") + print(line) + f.seek(0) s = f.readlines() registers = """ ETHMEM @@ -303,7 +457,7 @@ def modify_svd(builder_kwargs): """ - s.insert(-2, registers) + s.insert(line-1, registers) with open(builder_kwargs["csr_svd"], "w") as f: f.writelines(s) diff --git a/soc/leds.py b/soc/leds.py new file mode 100644 index 0000000..3920cf0 --- /dev/null +++ b/soc/leds.py @@ -0,0 +1,27 @@ + +import numpy as np +from migen import * +from litex.soc.interconnect.csr import AutoCSR, CSRStorage, CSRStatus, CSRField +from litex.soc.integration.doc import AutoDoc, ModuleDoc + +# LEDs --------------------------------------------------------------------------------------------- + +class Leds(Module, AutoCSR, AutoDoc): + """LED control. + 3 LEDs connected to random IOs + Attributes: + led_pin: Signals of the LED pin outputs. + led_polarity: Bit pattern to adjust polarity. 0 stays the same 1 inverts the signal. + led_name: Array of the LED names and descriptions. [["name1", "description1"], ["name2", "description2"]] + """ + def __init__(self, led_pin, led_polarity=0x00, led_name=[]): + # Documentation + self.intro = ModuleDoc("""LED control. + The LEDs are normal LEDs. Good information. :) + """) + + # HDL Implementationj + self._out = CSRStorage(len(led_pin), fields=[ + CSRField(fld[0], description=fld[1]) for fld in led_name + ]) + self.comb += led_pin.eq(self._out.storage ^ led_polarity) diff --git a/soc/pwm.py b/soc/pwm.py new file mode 100644 index 0000000..42e65d0 --- /dev/null +++ b/soc/pwm.py @@ -0,0 +1,35 @@ +# SingularitySurfer 2020 + + +import numpy as np +from migen import * +from litex.soc.interconnect.csr import AutoCSR, CSRStorage, CSRStatus, CSRField +from litex.soc.integration.doc import AutoDoc, ModuleDoc + + +class PWM(Module, AutoCSR): + """A simple PWM peripheral that uses a timer at sysclock. + + Parameters + ---------- + pin : output pin + width : width of the physical counter + """ + def __init__(self, pin, width=32): + + # Documentation + self.intro = ModuleDoc("""A simple PWM peripheral that uses a timer at sysclock. + PWM period software adjustable. If pwm output value larger that period -> kaputt. + """) + + self.period = CSRStorage(width, description='pwm period in sys cycles') + self.value = CSRStorage(width, description='pwm output value in sys cycles') + + + cnt = Signal(width) + + self.sync += { + cnt.eq(cnt+1), + If(cnt == self.period.storage, cnt.eq(0)), + pin.eq(cnt < self.value.storage) + } \ No newline at end of file