├── nexys_video ├── util │ ├── create_eth_ip.tcl │ ├── generate_mcs.tcl │ └── vivado_create_ip.tcl ├── rtl │ ├── muntjac_core_wrapper.sv │ ├── dvi.sv │ └── ddr.sv ├── Makefile └── data │ ├── pins.xdc │ └── device_tree.dts ├── firmware ├── src │ ├── fs │ │ └── mod.rs │ ├── entry.o │ ├── hart_mask.rs │ ├── memtest.rs │ ├── io.rs │ ├── timer.rs │ ├── block │ │ ├── part.rs │ │ └── mod.rs │ ├── iomem.rs │ ├── panic.rs │ ├── uart.rs │ ├── util.rs │ ├── ethernet.rs │ ├── memset.S │ ├── misalign.rs │ ├── memmove.S │ ├── interp.rs │ ├── fmt.rs │ ├── flash.rs │ ├── memcpy.S │ ├── video │ │ └── fbcon.rs │ ├── elf.rs │ ├── ipi.rs │ ├── allocator.rs │ └── video.rs ├── .gitignore ├── rust-toolchain.toml ├── .cargo │ └── config.toml ├── Cargo.toml └── linker.tpl.ld ├── data ├── bizcat.psf ├── rgb2dvi.tar ├── axi_ps2_1.0.tar.gz ├── mii_to_rmii_v2_0.tar └── debian_packages.txt ├── tools └── linker │ ├── Cargo.toml │ └── Cargo.lock ├── .gitignore ├── python-requirements.txt ├── genesys_2 ├── util │ ├── generate_mcs.tcl │ ├── create_eth_ip.tcl │ └── vivado_create_ip.tcl ├── Makefile ├── rtl │ ├── dvi.sv │ ├── muntjac_core_wrapper.sv │ └── ddr.sv └── data │ ├── device_tree.dts │ └── pins.xdc ├── nexys_a7 ├── util │ ├── generate_mcs.tcl │ ├── create_eth_ip.tcl │ └── vivado_create_ip.tcl ├── rtl │ └── muntjac_core_wrapper.sv ├── Makefile └── data │ ├── pins.xdc │ └── device_tree.dts ├── .gitmodules ├── Makefile ├── core_wrapper.core ├── flake.nix ├── LICENSE-MIT ├── util ├── generate_bitstream.tcl ├── create_rootfs.sh ├── program_bitstream.tcl └── program_flash.tcl ├── README.md ├── rtl ├── uncore │ ├── clint_tl.sv │ ├── plic_tl.sv │ ├── clint.sv │ └── plic.sv ├── gpio.sv ├── util │ └── seg_driver.sv ├── ps2.sv ├── sdhci.sv └── uart.sv ├── docs ├── ccx_architecture.md └── tutorial.md └── chip_top.core /nexys_video/util/create_eth_ip.tcl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /firmware/src/fs/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod ext; 2 | pub mod fat; 3 | -------------------------------------------------------------------------------- /firmware/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /device_tree.dts 3 | /linker.ld 4 | -------------------------------------------------------------------------------- /data/bizcat.psf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbdd0121/muntjac-soc/HEAD/data/bizcat.psf -------------------------------------------------------------------------------- /data/rgb2dvi.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbdd0121/muntjac-soc/HEAD/data/rgb2dvi.tar -------------------------------------------------------------------------------- /firmware/src/entry.o: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbdd0121/muntjac-soc/HEAD/firmware/src/entry.o -------------------------------------------------------------------------------- /data/axi_ps2_1.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbdd0121/muntjac-soc/HEAD/data/axi_ps2_1.0.tar.gz -------------------------------------------------------------------------------- /data/mii_to_rmii_v2_0.tar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nbdd0121/muntjac-soc/HEAD/data/mii_to_rmii_v2_0.tar -------------------------------------------------------------------------------- /firmware/rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly" 3 | target = "riscv64imac-unknown-none-elf" 4 | components = ["rust-src"] 5 | -------------------------------------------------------------------------------- /tools/linker/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "linker" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | object = "0.31" 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /vmlinux* 2 | /rootfs.img 3 | /build 4 | /*/build 5 | /*/target 6 | /*/device_tree.dts 7 | /*/firmware.* 8 | /*/bitstream.* 9 | vivado*.log 10 | vivado*.jou 11 | -------------------------------------------------------------------------------- /data/debian_packages.txt: -------------------------------------------------------------------------------- 1 | automake 2 | build-essential 3 | ethtool 4 | file 5 | gettext 6 | gdb 7 | initramfs-tools 8 | iperf3 9 | ntpdate 10 | openssh-server 11 | pkg-config 12 | psmisc 13 | python3 14 | strace 15 | sudo 16 | vim 17 | -------------------------------------------------------------------------------- /python-requirements.txt: -------------------------------------------------------------------------------- 1 | # Development version with OT-specific changes 2 | git+https://github.com/lowRISC/fusesoc.git@ot#egg=fusesoc >= 1.11.0 3 | 4 | # Development version with OT-specific changes 5 | git+https://github.com/lowRISC/edalize.git@ot#egg=edalize >= 0.2.0 6 | 7 | mako 8 | -------------------------------------------------------------------------------- /firmware/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "riscv64imac-unknown-none-elf" 3 | rustflags = ["-Clink-args=-Tlinker.ld --eh-frame-hdr", "-Clinker-flavor=ld", "-Cpanic=unwind", "-Csymbol-mangling-version=v0"] 4 | 5 | [unstable] 6 | build-std = ["core", "alloc"] 7 | 8 | [target.riscv64imac-unknown-none-elf] 9 | linker = "../build/linker" 10 | -------------------------------------------------------------------------------- /genesys_2/util/generate_mcs.tcl: -------------------------------------------------------------------------------- 1 | set mode [lindex $argv 0] 2 | 3 | if { $mode == "nobit" } { 4 | write_cfgmem -format mcs -size 32 -interface SPIx4 -loaddata {up 0x01000000 firmware.bin } -force -file firmware.mcs 5 | } else { 6 | write_cfgmem -format mcs -size 32 -interface SPIx4 -loadbit {up 0x00000000 bitstream.bit } -loaddata {up 0x01000000 firmware.bin } -force -file bitstream.mcs 7 | } 8 | -------------------------------------------------------------------------------- /nexys_a7/util/generate_mcs.tcl: -------------------------------------------------------------------------------- 1 | set mode [lindex $argv 0] 2 | 3 | if { $mode == "nobit" } { 4 | write_cfgmem -format mcs -size 16 -interface SPIx4 -loaddata {up 0x00800000 firmware.bin } -force -file firmware.mcs 5 | } else { 6 | write_cfgmem -format mcs -size 16 -interface SPIx4 -loadbit {up 0x00000000 bitstream.bit } -loaddata {up 0x00800000 firmware.bin } -force -file bitstream.mcs 7 | } 8 | -------------------------------------------------------------------------------- /nexys_video/util/generate_mcs.tcl: -------------------------------------------------------------------------------- 1 | set mode [lindex $argv 0] 2 | 3 | if { $mode == "nobit" } { 4 | write_cfgmem -format mcs -size 32 -interface SPIx4 -loaddata {up 0x01000000 firmware.bin } -force -file firmware.mcs 5 | } else { 6 | write_cfgmem -format mcs -size 32 -interface SPIx4 -loadbit {up 0x00000000 bitstream.bit } -loaddata {up 0x01000000 firmware.bin } -force -file bitstream.mcs 7 | } 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "sdhci"] 2 | path = sdhci 3 | url = git@github.com:nbdd0121/sdhci.git 4 | [submodule "muntjac"] 5 | path = muntjac 6 | url = git@github.com:lowrisc/muntjac.git 7 | [submodule "linux"] 8 | path = linux 9 | url = git@github.com:nbdd0121/linux.git 10 | branch = muntjac-6.6 11 | shallow = true 12 | [submodule "display_controller"] 13 | path = display_controller 14 | url = git@github.com:nbdd0121/display_controller.git 15 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LD_VERSION := $(shell riscv64-unknown-linux-gnu-ld -v 2>/dev/null) 2 | 3 | ifdef LD_VERSION 4 | PREFIX = riscv64-unknown-linux-gnu- 5 | else 6 | PREFIX = riscv64-linux-gnu- 7 | endif 8 | 9 | linux/.config: 10 | cp data/linux.config $@ 11 | 12 | vmlinux: linux/.config 13 | cd linux; $(MAKE) CROSS_COMPILE=$(PREFIX) ARCH=riscv 14 | $(PREFIX)strip linux/vmlinux -o $@ 15 | 16 | vmlinux.gz: vmlinux 17 | gzip < $< > $@ 18 | 19 | rootfs.img: data/debian_packages.txt 20 | touch -a $@ 21 | util/create_rootfs.sh 22 | 23 | CARGO_OUT_DIR=$(realpath .)/build/release 24 | 25 | # Files colleted by Cargo 26 | -include $(CARGO_OUT_DIR)/linker.d 27 | 28 | $(CARGO_OUT_DIR)/linker: 29 | cd tools/linker; CARGO_TARGET_DIR=$(abspath ./build) cargo build --release 30 | touch $@ 31 | 32 | build/linker: $(CARGO_OUT_DIR)/linker 33 | cp $< $@ 34 | -------------------------------------------------------------------------------- /firmware/src/hart_mask.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Copy)] 2 | pub struct HartMask { 3 | pub mask: usize, 4 | pub mask_base: usize, 5 | } 6 | 7 | impl HartMask { 8 | pub fn new(mask: usize, mask_base: usize) -> Self { 9 | Self { mask, mask_base } 10 | } 11 | 12 | pub fn is_set(&self, hart_id: usize) -> bool { 13 | match hart_id.checked_sub(self.mask_base) { 14 | None => false, 15 | Some(v) => { 16 | if v < core::mem::size_of::() * 8 { 17 | (self.mask >> v) & 1 != 0 18 | } else { 19 | false 20 | } 21 | } 22 | } 23 | } 24 | 25 | pub fn normalize(&self) -> usize { 26 | let mut ret = 0; 27 | for i in 0..4 { 28 | ret |= (self.is_set(i) as usize) << i; 29 | } 30 | ret 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /core_wrapper.core: -------------------------------------------------------------------------------- 1 | CAPI=2: 2 | # SPDX-License-Identifier: MIT OR Apache-2.0 3 | name: "garyguo.net:systems:muntjac_core_wrapper:0.1" 4 | description: "Muntjac Core Wrapper" 5 | filesets: 6 | files_rtl: 7 | depend: 8 | - lowrisc:muntjac:core 9 | file_type: systemVerilogSource 10 | 11 | files_rtl_nexys_a7: 12 | files: 13 | - nexys_a7/rtl/muntjac_core_wrapper.sv 14 | file_type: systemVerilogSource 15 | 16 | files_rtl_nexys_video: 17 | files: 18 | - nexys_video/rtl/muntjac_core_wrapper.sv 19 | file_type: systemVerilogSource 20 | 21 | files_rtl_genesys_2: 22 | files: 23 | - genesys_2/prebuild/muntjac_core_preset1.vm 24 | - genesys_2/rtl/muntjac_core_wrapper.sv 25 | file_type: systemVerilogSource 26 | 27 | targets: 28 | default: &default_target 29 | filesets: 30 | - files_rtl 31 | - target_nexys_a7 ? (files_rtl_nexys_a7) 32 | - target_nexys_video ? (files_rtl_nexys_video) 33 | - target_genesys_2 ? (files_rtl_genesys_2) 34 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs = { 3 | lowrisc-nix.url = "github:lowRISC/lowrisc-nix"; 4 | nixpkgs.follows = "lowrisc-nix/nixpkgs"; 5 | flake-utils.follows = "lowrisc-nix/flake-utils"; 6 | }; 7 | outputs = { 8 | self, 9 | nixpkgs, 10 | flake-utils, 11 | lowrisc-nix, 12 | }: let 13 | all_system_outputs = flake-utils.lib.eachDefaultSystem ( 14 | system: let 15 | pkgs = import nixpkgs { 16 | inherit system; 17 | }; 18 | lowriscPkgs = lowrisc-nix.outputs.packages.${system}; 19 | in { 20 | formatter = pkgs.alejandra; 21 | devShells.default = pkgs.mkShellNoCC { 22 | packages = 23 | (with pkgs; [ 24 | screen 25 | dtc 26 | pkgsCross.riscv64.buildPackages.gcc 27 | ]) 28 | ++ (with lowriscPkgs; [ 29 | python_ot 30 | lowrisc-toolchain-gcc-rv64imac 31 | ]); 32 | }; 33 | } 34 | ); 35 | in 36 | all_system_outputs; 37 | } 38 | -------------------------------------------------------------------------------- /firmware/src/memtest.rs: -------------------------------------------------------------------------------- 1 | use core::mem::size_of; 2 | 3 | /// Test a range of memory for errors. 4 | pub fn memtest(range: &mut [usize]) { 5 | let min = range.as_ptr() as usize; 6 | let max = min + range.len() * core::mem::size_of::(); 7 | 8 | println!("Memory check in progress"); 9 | println!("Writing..."); 10 | for page in range.chunks_mut(0x200000 / size_of::()) { 11 | for dword in page { 12 | unsafe { core::ptr::write_volatile(dword, dword as *mut usize as usize) }; 13 | } 14 | print!("."); 15 | } 16 | println!("\nReading..."); 17 | for page in (min..max).step_by(0x200000) { 18 | for dword in (page..(page + 0x200000)).step_by(8) { 19 | let read = unsafe { core::ptr::read_volatile(dword as *mut usize) }; 20 | if read != dword { 21 | println!("Incorrect value: {:x} = {:x}", dword, read); 22 | return; 23 | } 24 | } 25 | print!("."); 26 | } 27 | println!("\nMemory check completed"); 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /firmware/src/io.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub enum Error { 3 | Textual(&'static str), 4 | } 5 | 6 | pub type Result = core::result::Result; 7 | 8 | pub trait Read { 9 | fn read(&mut self, buf: &mut [u8]) -> Result; 10 | 11 | fn read_exact(&mut self, mut buf: &mut [u8]) -> Result<()> { 12 | while !buf.is_empty() { 13 | let size = self.read(buf)?; 14 | if size == 0 { 15 | return Err(Error::Textual("early eof")); 16 | } 17 | buf = &mut buf[size..]; 18 | } 19 | Ok(()) 20 | } 21 | } 22 | 23 | pub trait ReadAt { 24 | fn read_at(&mut self, buf: &mut [u8], offset: u64) -> Result; 25 | 26 | fn read_exact_at(&mut self, mut buf: &mut [u8], mut offset: u64) -> Result<()> { 27 | while !buf.is_empty() { 28 | let size = self.read_at(buf, offset)?; 29 | if size == 0 { 30 | return Err(Error::Textual("early eof")); 31 | } 32 | buf = &mut buf[size..]; 33 | offset += size as u64; 34 | } 35 | Ok(()) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /util/generate_bitstream.tcl: -------------------------------------------------------------------------------- 1 | if { [get_property needs_refresh [get_runs synth_1]] } { 2 | reset_runs synth_1 3 | } 4 | 5 | if { [get_property progress [get_runs synth_1]] != "100%"} { 6 | launch_runs synth_1 7 | wait_on_run synth_1 8 | puts "Synthesis completed" 9 | } else { 10 | puts "Synthesis already complete" 11 | } 12 | 13 | if { [get_property progress [get_runs synth_1]] != "100%"} { 14 | puts "ERROR: Synthesis failed." 15 | exit 1 16 | } 17 | 18 | if { [get_property needs_refresh [get_runs impl_1]] } { 19 | reset_runs impl_1 20 | } 21 | 22 | if { [get_property progress [get_runs impl_1]] != "100%"} { 23 | launch_runs impl_1 -to_step write_bitstream 24 | wait_on_run impl_1 25 | puts "Bitstream generation completed" 26 | } else { 27 | puts "Bitstream generation already complete" 28 | } 29 | 30 | if { [get_property progress [get_runs impl_1]] != "100%"} { 31 | puts "ERROR: Implementation and bitstream generation step failed." 32 | exit 1 33 | } 34 | 35 | set vivadoDefaultBitstreamFile [ get_property DIRECTORY [current_run] ]/[ get_property top [current_fileset] ].bit 36 | file copy -force $vivadoDefaultBitstreamFile [pwd]/[current_project].bit 37 | -------------------------------------------------------------------------------- /firmware/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bootloader" 3 | version = "0.1.0" 4 | authors = ["Gary Guo "] 5 | edition = "2021" 6 | 7 | [build-dependencies] 8 | cc = "1" 9 | rand = "0.8" 10 | regex = "1.5" 11 | fdt = "0.1.3" 12 | 13 | [dependencies] 14 | byteorder = { version = "1", default-features = false } 15 | arrayvec = { version = "0.7", default-features = false } 16 | softfp = { version = "0.1.0", default-features = false } 17 | riscv = { git = "https://github.com/nbdd0121/r2vm.git" } 18 | buddy = { git = "https://github.com/nbdd0121/buddy.git" } 19 | ro_cell = "0.1" 20 | spin = "0.9" 21 | log = { version = "0.4", default-features = false } 22 | unwinding = { version = "0.2.7", default-features = false, features = ["unwinder", "personality", "fde-gnu-eh-frame-hdr", "panicking"] } 23 | psf2 = { version = "0.4", default-features = false, optional = true } 24 | 25 | [features] 26 | fp-mem = [] 27 | fp-none = ["fp-mem"] 28 | fbcon = ["psf2"] 29 | default = ["fp-none", "fbcon"] 30 | 31 | [profile.release] 32 | opt-level = 3 33 | lto = true 34 | debug = true 35 | panic = "unwind" 36 | 37 | [lints.rust] 38 | unexpected_cfgs = { level = "warn", check-cfg = ['cfg(has_display)'] } 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Muntjac SoC 2 | 3 | This repository contains a simple SoC that builds upon [muntjac](https://github.com/lowRISC/muntjac) that can be used out-of-the-box. 4 | 5 | For a detailed walkthough about how to build/use this project, please check the [tutorial](./docs/tutorial.md). A description of the core complex can be found [here](./docs/ccx_architecture.md). 6 | 7 | ## Components 8 | 9 | The SoC contains the following components out of the box: 10 | * 2 Muntjac cores 11 | * Interrupt controller and timer (PLIC and CLINT) 12 | * Flash memory controller[^1] (work in XIP mode) 13 | * DDR memory controller[^1] 14 | * UART 16550[^1] 15 | * SD card controller 16 | * PS/2 controller[^3] 17 | * *(optional)* Ethernet controller[^2] 18 | * *(optional)* Display controller[^3] 19 | 20 | [^1]: Xilinx IP that comes with Vivado, but not open source 21 | [^2]: Xilinx IP, not open source, separate license required 22 | [^3]: For Nexys Video and Genesys 2 boards only 23 | 24 | ## Supported Targets 25 | 26 | Currently these FPGA boards are supported: 27 | * Nexys A7 28 | * Nexys Video 29 | * Genesys 2 30 | 31 | ## Licensing 32 | 33 | Unless otherwise noted, everything in this repository is dual-licensed in the MIT license and the Apache 34 | License, Version 2.0. See [LICENSE-APACHE](./LICENSE-APACHE), [LICENSE-MIT](./LICENSE-MIT) for details. 35 | -------------------------------------------------------------------------------- /firmware/src/timer.rs: -------------------------------------------------------------------------------- 1 | use core::time::Duration; 2 | 3 | use super::address::CLINT_BASE; 4 | 5 | pub fn time_u64() -> u64 { 6 | // We assume I/O are 32-bits, so this prevents HI change while we are reading low 7 | let time = 'l: loop { 8 | let hi = unsafe { core::ptr::read_volatile((CLINT_BASE + 0xBFFC) as *const u32) }; 9 | let lo = unsafe { core::ptr::read_volatile((CLINT_BASE + 0xBFF8) as *const u32) }; 10 | let hi2 = unsafe { core::ptr::read_volatile((CLINT_BASE + 0xBFFC) as *const u32) }; 11 | if hi == hi2 { 12 | break 'l (hi as u64) << 32 | lo as u64; 13 | } 14 | }; 15 | time 16 | } 17 | 18 | pub fn set_timer_u64(hart: usize, time: u64) { 19 | unsafe { 20 | // This is safe because it's okay if 64-bit write is broken into halves. 21 | core::ptr::write_volatile((CLINT_BASE + 0x4000 + hart * 8) as *mut u64, time); 22 | } 23 | } 24 | 25 | pub fn time() -> Duration { 26 | Duration::from_micros(time_u64()) 27 | } 28 | 29 | pub fn sleep(duration: Duration) { 30 | let timer = Timer::new(duration); 31 | while !timer.fired() {} 32 | } 33 | 34 | pub struct Timer(pub Duration); 35 | 36 | impl Timer { 37 | pub fn new(duration: Duration) -> Self { 38 | Timer(time() + duration) 39 | } 40 | 41 | pub fn fired(&self) -> bool { 42 | self.0 < time() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /firmware/src/block/part.rs: -------------------------------------------------------------------------------- 1 | use super::Block; 2 | use crate::io::Result as IoResult; 3 | use alloc::sync::Arc; 4 | 5 | pub struct Part { 6 | blk: Arc, 7 | offset: u64, 8 | } 9 | 10 | impl Part { 11 | pub fn new(blk: Arc, offset: u64) -> Self { 12 | Self { blk, offset } 13 | } 14 | 15 | pub fn first_partition(blk: Arc) -> IoResult { 16 | let mut buf = [0; 512]; 17 | 18 | // Read the MBR 19 | blk.read_exact_at(&mut buf, 0)?; 20 | 21 | assert_eq!(buf[510], 0x55, "MBR not valid"); 22 | assert_eq!(buf[511], 0xAA, "MBR not valid"); 23 | 24 | // Check partition 1 25 | let part = &buf[0x1BE..0x1CE]; 26 | assert_eq!(part[4], 0x0C, "Only FAT32 is supported"); 27 | let mut lba = [0; 4]; 28 | lba.copy_from_slice(&part[0x8..0xC]); 29 | let lba = u32::from_le_bytes(lba); 30 | let part_offset = lba as u64 * 512; 31 | 32 | println!("FAT32 partition located at {}KiB", lba / 2); 33 | Ok(Self::new(blk, part_offset)) 34 | } 35 | } 36 | 37 | impl super::Block for Part { 38 | fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> IoResult<()> { 39 | self.blk.read_exact_at(buf, offset + self.offset) 40 | } 41 | 42 | fn write_all_at(&self, buf: &[u8], offset: u64) -> IoResult<()> { 43 | self.blk.write_all_at(buf, offset + self.offset) 44 | } 45 | 46 | fn len(&self) -> u64 { 47 | todo!(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /rtl/uncore/clint_tl.sv: -------------------------------------------------------------------------------- 1 | module clint_tl #( 2 | parameter NumHarts = 1, 3 | parameter TimerClockFrequency = 40, 4 | 5 | parameter AddrWidth = 16, 6 | parameter SourceWidth = 1 7 | ) ( 8 | input logic clk_i, 9 | input logic rst_ni, 10 | input logic timer_clk_i, 11 | 12 | // IRQ output for each hart. 13 | output logic [NumHarts-1:0] msip_o, 14 | output logic [NumHarts-1:0] mtip_o, 15 | 16 | `TL_DECLARE_DEVICE_PORT(64, AddrWidth, SourceWidth, 1, link) 17 | ); 18 | 19 | logic bram_en; 20 | logic bram_we; 21 | logic [7:0] bram_wmask; 22 | logic [12:0] bram_addr; 23 | logic [63:0] bram_wrdata; 24 | logic [63:0] bram_rddata; 25 | 26 | clint #( 27 | .NUM_HARTS (NumHarts), 28 | .CLK_FREQ (TimerClockFrequency) 29 | ) clint ( 30 | .clk (clk_i), 31 | .rstn (rst_ni), 32 | .bram_addr ({bram_addr, 3'b0}), 33 | .bram_en (bram_en), 34 | .bram_we (bram_we ? bram_wmask : '0), 35 | .bram_rddata (bram_rddata), 36 | .bram_wrdata (bram_wrdata), 37 | .timer_clk (timer_clk_i), 38 | .msip (msip_o), 39 | .mtip (mtip_o) 40 | ); 41 | 42 | tl_adapter_bram #( 43 | .DataWidth (64), 44 | .AddrWidth (AddrWidth), 45 | .SourceWidth (SourceWidth), 46 | .BramAddrWidth (13) 47 | ) bridge ( 48 | .clk_i, 49 | .rst_ni, 50 | `TL_FORWARD_DEVICE_PORT(host, link), 51 | .bram_en_o (bram_en), 52 | .bram_we_o (bram_we), 53 | .bram_wmask_o (bram_wmask), 54 | .bram_addr_o (bram_addr), 55 | .bram_wdata_o (bram_wrdata), 56 | .bram_rdata_i (bram_rddata) 57 | ); 58 | 59 | endmodule 60 | -------------------------------------------------------------------------------- /docs/ccx_architecture.md: -------------------------------------------------------------------------------- 1 | ## Core complex architecture 2 | 3 | The [core complex](/rtl/ccx.sv) of this SoC is divided into the following modules: 4 | * [rom_adapter](https://github.com/lowRISC/muntjac/blob/master/ip/tl/rtl/tl_adapter.sv), [mem_adapter](https://github.com/lowRISC/muntjac/blob/master/ip/tl/rtl/tl_adapter.sv) and [io_adapter](https://github.com/lowRISC/muntjac/blob/master/ip/tl/rtl/tl_adapter.sv) : adapter for TileLink links with different parameters 5 | * [rom_term](https://github.com/lowRISC/muntjac/blob/master/ip/tl/rtl/tl_rom_terminator.sv), [mem_term](https://github.com/lowRISC/muntjac/blob/master/ip/tl/rtl/tl_ram_terminator.sv) and [io_term](https://github.com/lowRISC/muntjac/blob/master/ip/tl/rtl/tl_io_terminator.sv): TL-C bus (TileLink Cached) to TL-UL bus (TileLink Uncached Lightweight) converter 6 | * [llc_inst](https://github.com/lowRISC/muntjac/blob/master/ip/core/rtl/muntjac_llc.sv): L2 (last level cache) shared by all the cores 7 | * [socket_1n](https://github.com/lowRISC/muntjac/blob/master/ip/tl/rtl/tl_socket_1n.sv): single to multiple TL bus converter 8 | * [host_aggregator](https://github.com/lowRISC/muntjac/blob/master/ip/tl/rtl/tl_socket_m1.sv): multiple to single TL bus converter 9 | * [core n](https://github.com/lowRISC/muntjac/blob/master/ip/core/rtl/muntjac_core.sv): one Muntjac RISC-V core 10 | * [dcache_inst](https://github.com/lowRISC/muntjac/blob/master/ip/core/rtl/muntjac_dcache.sv): L1 data cache 11 | * [icache_inst](https://github.com/lowRISC/muntjac/blob/master/ip/core/rtl/muntjac_icache.sv): L1 instruction cache 12 | * [frontend](https://github.com/lowRISC/muntjac/blob/master/ip/pipeline/rtl/muntjac_frontend.sv): pipeline frontend (instruction cache related) 13 | * [backend](https://github.com/lowRISC/muntjac/blob/master/ip/pipeline/rtl/muntjac_backend.sv): pipeline backend (data cache related) 14 | 15 | ![ccx](/docs/assets/ccx_schem.svg) 16 | -------------------------------------------------------------------------------- /firmware/src/iomem.rs: -------------------------------------------------------------------------------- 1 | use core::mem; 2 | use core::ptr; 3 | 4 | #[derive(Clone, Copy)] 5 | pub struct IoMem { 6 | ptr: usize, 7 | } 8 | 9 | #[allow(dead_code)] 10 | impl IoMem { 11 | #[inline] 12 | pub const unsafe fn new(ptr: usize) -> Self { 13 | Self { ptr } 14 | } 15 | 16 | #[inline] 17 | pub fn read(&self, offset: usize) -> T { 18 | assert!(offset + mem::size_of::() <= SIZE); 19 | assert!(offset % mem::align_of::() == 0); 20 | unsafe { ptr::read_volatile((self.ptr + offset) as *const T) } 21 | } 22 | 23 | #[inline] 24 | pub fn read_u8(&self, offset: usize) -> u8 { 25 | self.read(offset) 26 | } 27 | 28 | #[inline] 29 | pub fn read_u16(&self, offset: usize) -> u16 { 30 | self.read(offset) 31 | } 32 | 33 | #[inline] 34 | pub fn read_u32(&self, offset: usize) -> u32 { 35 | self.read(offset) 36 | } 37 | 38 | #[inline] 39 | pub fn read_u64(&self, offset: usize) -> u64 { 40 | self.read(offset) 41 | } 42 | 43 | #[inline] 44 | pub fn write(&self, offset: usize, value: T) { 45 | assert!(offset + mem::size_of::() <= SIZE); 46 | assert!(offset % mem::align_of::() == 0); 47 | unsafe { ptr::write_volatile((self.ptr + offset) as *mut T, value) }; 48 | } 49 | 50 | #[inline] 51 | pub fn write_u8(&self, offset: usize, value: u8) { 52 | self.write(offset, value) 53 | } 54 | 55 | #[inline] 56 | pub fn write_u16(&self, offset: usize, value: u16) { 57 | self.write(offset, value) 58 | } 59 | 60 | #[inline] 61 | pub fn write_u32(&self, offset: usize, value: u32) { 62 | self.write(offset, value) 63 | } 64 | 65 | #[inline] 66 | pub fn write_u64(&self, offset: usize, value: u64) { 67 | self.write(offset, value) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /rtl/gpio.sv: -------------------------------------------------------------------------------- 1 | `include "tl_util.svh" 2 | 3 | module gpio # ( 4 | parameter AddrWidth = 12, 5 | parameter SourceWidth = 1, 6 | parameter NumGpios = 32 7 | ) ( 8 | // Clock and reset 9 | input clk_i, 10 | input rst_ni, 11 | 12 | // IO ports. They are expected to be presynchronized to clk_i. 13 | input logic [NumGpios-1:0] gpio_i, 14 | output logic [NumGpios-1:0] gpio_o, 15 | output logic [NumGpios-1:0] gpio_t, 16 | 17 | // TileLink port 18 | `TL_DECLARE_DEVICE_PORT(32, AddrWidth, SourceWidth, 1, link) 19 | ); 20 | 21 | logic bram_en; 22 | logic bram_we; 23 | logic [3:0] bram_wmask; 24 | logic [0:0] bram_addr; 25 | logic [31:0] bram_wrdata; 26 | logic [31:0] bram_rddata; 27 | 28 | always_ff @(posedge clk_i or negedge rst_ni) begin 29 | if (!rst_ni) begin 30 | gpio_o <= '0; 31 | gpio_t <= '1; 32 | end else begin 33 | if (bram_en) begin 34 | bram_rddata <= '0; 35 | unique case (bram_addr) 36 | 1'b0: begin 37 | // Select gpio_i or gpio_o based on gpio_t 38 | bram_rddata <= (gpio_t & gpio_i) | (~gpio_t & gpio_o); 39 | if (bram_we && |bram_wmask) begin 40 | gpio_o <= bram_wrdata; 41 | end 42 | end 43 | 1'b1: begin 44 | bram_rddata <= ~gpio_t; 45 | if (bram_we && |bram_wmask) begin 46 | gpio_t <= ~bram_wrdata; 47 | end 48 | end 49 | endcase 50 | end 51 | end 52 | end 53 | 54 | // Terminate TileLink and convert to BRAM access. 55 | 56 | tl_adapter_bram #( 57 | .DataWidth (32), 58 | .AddrWidth (AddrWidth), 59 | .SourceWidth (SourceWidth), 60 | .BramAddrWidth (1) 61 | ) bridge ( 62 | .clk_i, 63 | .rst_ni, 64 | `TL_FORWARD_DEVICE_PORT(host, link), 65 | .bram_en_o (bram_en), 66 | .bram_we_o (bram_we), 67 | .bram_wmask_o (bram_wmask), 68 | .bram_addr_o (bram_addr), 69 | .bram_wdata_o (bram_wrdata), 70 | .bram_rdata_i (bram_rddata) 71 | ); 72 | 73 | endmodule 74 | -------------------------------------------------------------------------------- /firmware/src/panic.rs: -------------------------------------------------------------------------------- 1 | use core::cell::{Cell, UnsafeCell}; 2 | use core::ffi::c_void; 3 | use core::mem::MaybeUninit; 4 | use core::panic::PanicInfo; 5 | use unwinding::{abi::*, panicking}; 6 | 7 | #[thread_local] 8 | static PANIC_COUNT: Cell = Cell::new(0); 9 | 10 | #[thread_local] 11 | static EXCEPTION_STORAGE: UnsafeCell> = 12 | UnsafeCell::new(MaybeUninit::uninit()); 13 | 14 | fn stack_trace() { 15 | struct CallbackData { 16 | counter: usize, 17 | } 18 | extern "C" fn callback( 19 | unwind_ctx: &UnwindContext<'_>, 20 | arg: *mut c_void, 21 | ) -> UnwindReasonCode { 22 | let data = unsafe { &mut *(arg as *mut CallbackData) }; 23 | data.counter += 1; 24 | println!("{:4}:{:#19x}", data.counter, _Unwind_GetIP(unwind_ctx)); 25 | UnwindReasonCode::NO_REASON 26 | } 27 | let mut data = CallbackData { counter: 0 }; 28 | _Unwind_Backtrace(callback, &mut data as *mut _ as _); 29 | } 30 | 31 | struct Panic; 32 | 33 | unsafe impl panicking::Exception for Panic { 34 | const CLASS: [u8; 8] = *b"noneRUST"; 35 | 36 | fn wrap(_: Self) -> *mut UnwindException { 37 | EXCEPTION_STORAGE.get() as *mut UnwindException 38 | } 39 | 40 | unsafe fn unwrap(_: *mut UnwindException) -> Self { 41 | Panic 42 | } 43 | } 44 | 45 | #[panic_handler] 46 | pub fn panic(info: &PanicInfo<'_>) -> ! { 47 | println!("{}", info); 48 | stack_trace(); 49 | 50 | // Update panic count. 51 | if PANIC_COUNT.get() >= 1 { 52 | println!("panicked while processing panic. aborting."); 53 | } 54 | PANIC_COUNT.set(1); 55 | 56 | match panicking::begin_panic(Panic) { 57 | UnwindReasonCode::END_OF_STACK => { 58 | println!("uncaught exception, aborting."); 59 | } 60 | code => println!("failed to initiate panic, error {}", code.0), 61 | } 62 | super::abort(); 63 | } 64 | 65 | #[allow(dead_code)] 66 | pub fn catch_unwind R>(f: F) -> Result { 67 | panicking::catch_unwind::(f).map_err(|_| ()) 68 | } 69 | -------------------------------------------------------------------------------- /rtl/util/seg_driver.sv: -------------------------------------------------------------------------------- 1 | module seg_driver ( 2 | input clk, 3 | input resetn, 4 | 5 | input [31:0] number, 6 | output logic [6:0] segments, 7 | output logic [7:0] nibble_enable 8 | ); 9 | 10 | // Cycle an index 11 | logic [2:0] index; 12 | logic [16:0] counter; 13 | always_ff @(posedge clk or negedge resetn) 14 | if (!resetn) begin 15 | index <= 3'b0; 16 | counter <= '0; 17 | end 18 | else begin 19 | counter <= counter + 1; 20 | // Refresh cycle is 1 to 16ms 21 | // Each nibble should be enabled every 1/8 refresh cycle 22 | // i.e. at least 1/8 ms. 23 | // 100MHz * (1/8 ms) = 12500 cycles 24 | if (counter == 12499) begin 25 | index <= index + 1; 26 | counter <= 0; 27 | end 28 | end 29 | 30 | // Convert index to one-hot 31 | always_comb begin 32 | nibble_enable = '1; 33 | nibble_enable[index] = 1'b0; 34 | end 35 | 36 | // Pick up the nibble to display 37 | logic [7:0][3:0] nibbles; 38 | logic [3:0] current_nibble; 39 | assign nibbles = number; 40 | assign current_nibble = nibbles[index]; 41 | 42 | // Set segments 43 | always_comb 44 | unique case (current_nibble) 45 | 4'b0000: segments = 7'b0000001; 46 | 4'b0001: segments = 7'b1001111; 47 | 4'b0010: segments = 7'b0010010; 48 | 4'b0011: segments = 7'b0000110; 49 | 4'b0100: segments = 7'b1001100; 50 | 4'b0101: segments = 7'b0100100; 51 | 4'b0110: segments = 7'b0100000; 52 | 4'b0111: segments = 7'b0001111; 53 | 4'b1000: segments = 7'b0000000; 54 | 4'b1001: segments = 7'b0000100; 55 | 4'b1010: segments = 7'b0001000; 56 | 4'b1011: segments = 7'b1100000; 57 | 4'b1100: segments = 7'b0110001; 58 | 4'b1101: segments = 7'b1000010; 59 | 4'b1110: segments = 7'b0110000; 60 | 4'b1111: segments = 7'b0111000; 61 | endcase 62 | 63 | endmodule 64 | -------------------------------------------------------------------------------- /rtl/ps2.sv: -------------------------------------------------------------------------------- 1 | `include "tl_util.svh" 2 | `include "axi_util.svh" 3 | `include "axi_lite_util.svh" 4 | 5 | module ps2 # ( 6 | parameter SourceWidth = 1 7 | ) ( 8 | // Clock and reset 9 | input clk_i, 10 | input rst_ni, 11 | 12 | // IO ports 13 | inout ps2_clk, 14 | inout ps2_dat, 15 | 16 | // TileLink port 17 | `TL_DECLARE_DEVICE_PORT(32, 5, SourceWidth, 1, link), 18 | 19 | // Interrupt 20 | output irq_o 21 | ); 22 | 23 | logic ps2_clk_i; 24 | logic ps2_clk_o; 25 | logic ps2_clk_t; 26 | logic ps2_dat_i; 27 | logic ps2_dat_o; 28 | logic ps2_dat_t; 29 | 30 | IOBUF ps2_clk_iobuf ( 31 | .I (ps2_clk_o), 32 | .IO (ps2_clk), 33 | .O (ps2_clk_i), 34 | .T (ps2_clk_t) 35 | ); 36 | 37 | IOBUF ps2_dat_iobuf ( 38 | .I (ps2_dat_o), 39 | .IO (ps2_dat), 40 | .O (ps2_dat_i), 41 | .T (ps2_dat_t) 42 | ); 43 | 44 | `AXI_LITE_DECLARE(32, 5, axi); 45 | 46 | axi_ps2_0 ( 47 | .PS2_Data_I (ps2_dat_i), 48 | .PS2_Data_O (ps2_dat_o), 49 | .PS2_Data_T (ps2_dat_t), 50 | .PS2_Clk_I (ps2_clk_i), 51 | .PS2_Clk_O (ps2_clk_o), 52 | .PS2_Clk_T (ps2_clk_t), 53 | .PS2_interrupt (irq_o), 54 | .S_AXI_awaddr (axi_aw.addr), 55 | .S_AXI_awprot (axi_aw.prot), 56 | .S_AXI_awvalid (axi_aw_valid), 57 | .S_AXI_awready (axi_aw_ready), 58 | .S_AXI_wdata (axi_w.data), 59 | .S_AXI_wstrb (axi_w.strb), 60 | .S_AXI_wvalid (axi_w_valid), 61 | .S_AXI_wready (axi_w_ready), 62 | .S_AXI_bresp (axi_b.resp), 63 | .S_AXI_bvalid (axi_b_valid), 64 | .S_AXI_bready (axi_b_ready), 65 | .S_AXI_araddr (axi_ar.addr), 66 | .S_AXI_arprot (axi_ar.prot), 67 | .S_AXI_arvalid (axi_ar_valid), 68 | .S_AXI_arready (axi_ar_ready), 69 | .S_AXI_rdata (axi_r.data), 70 | .S_AXI_rresp (axi_r.resp), 71 | .S_AXI_rvalid (axi_r_valid), 72 | .S_AXI_rready (axi_r_ready), 73 | .S_AXI_aclk (clk_i), 74 | .S_AXI_aresetn (rst_ni) 75 | ); 76 | 77 | tl_axi_lite_adapter #( 78 | .DataWidth (32), 79 | .AddrWidth (5), 80 | .SourceWidth (SourceWidth) 81 | ) adapter ( 82 | .clk_i, 83 | .rst_ni, 84 | `TL_FORWARD_DEVICE_PORT(host, link), 85 | `AXI_CONNECT_HOST_PORT(device, axi) 86 | ); 87 | 88 | endmodule 89 | -------------------------------------------------------------------------------- /firmware/src/uart.rs: -------------------------------------------------------------------------------- 1 | const UART_RBR: usize = 0x0000; 2 | const UART_THR: usize = 0x0000; 3 | const UART_FCR: usize = 0x0008; 4 | const UART_LSR: usize = 0x0014; 5 | const UART_LCR: usize = 0x000C; 6 | const UART_DLL: usize = 0x0000; 7 | const UART_DLM: usize = 0x0004; 8 | 9 | #[inline] 10 | fn reg(addr: usize) -> *mut u32 { 11 | (super::address::UART_BASE + addr) as _ 12 | } 13 | 14 | pub fn uart_init() { 15 | // Reset FIFO 16 | unsafe { 17 | core::ptr::write_volatile(reg(UART_FCR), 0b111); 18 | } 19 | 20 | // Set baud to 230,400 8N1 21 | // 18.432M / (16 * 230,400) = 5 22 | uart_set_mode(Config { 23 | divisor: 5, 24 | lcr: 0b11, 25 | }); 26 | } 27 | 28 | #[derive(Clone, Copy)] 29 | pub struct Config { 30 | pub divisor: u16, 31 | pub lcr: u8, 32 | } 33 | 34 | pub fn uart_set_mode(config: Config) { 35 | unsafe { 36 | core::ptr::write_volatile(reg(UART_LCR), (config.lcr | 0x80) as u32); 37 | core::ptr::write_volatile(reg(UART_DLL), (config.divisor & 0xff) as u32); 38 | core::ptr::write_volatile(reg(UART_DLM), (config.divisor >> 8) as u32); 39 | core::ptr::write_volatile(reg(UART_LCR), config.lcr as u32); 40 | } 41 | } 42 | 43 | pub fn uart_get_mode() -> Config { 44 | unsafe { 45 | let lcr = (core::ptr::read_volatile(reg(UART_LCR)) as u8) & !0x80; 46 | core::ptr::write_volatile(reg(UART_LCR), (lcr | 0x80) as u32); 47 | let dll = core::ptr::read_volatile(reg(UART_DLL)) as u8; 48 | let dlm = core::ptr::read_volatile(reg(UART_DLM)) as u8; 49 | core::ptr::write_volatile(reg(UART_LCR), lcr as u32); 50 | Config { 51 | divisor: (dlm as u16) << 8 | dll as u16, 52 | lcr, 53 | } 54 | } 55 | } 56 | 57 | pub fn uart_send_byte(byte: u8) { 58 | unsafe { 59 | while core::ptr::read_volatile(reg(UART_LSR)) & 0x20 == 0 {} 60 | core::ptr::write_volatile(reg(UART_THR), byte as u32); 61 | } 62 | } 63 | 64 | pub fn uart_try_recv_byte() -> Option { 65 | unsafe { 66 | if core::ptr::read_volatile(reg(UART_LSR)) & 0x01 != 0 { 67 | let data = core::ptr::read_volatile(reg(UART_RBR)); 68 | Some(data as u8) 69 | } else { 70 | None 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /rtl/uncore/plic_tl.sv: -------------------------------------------------------------------------------- 1 | module plic_tl #( 2 | parameter NumContexts = 1, 3 | // Currently can only be 32. 4 | parameter NumIrqs = 32, 5 | 6 | parameter AddrWidth = 22, 7 | parameter SourceWidth = 1 8 | ) ( 9 | input logic clk_i, 10 | input logic rst_ni, 11 | 12 | // Interrupt sources. Bit 0 is ignored. 13 | input logic [NumIrqs-1:0] interrupts_i, 14 | 15 | // Whether each interrupt should be level triggered or edge triggered 16 | input logic [NumIrqs-1:0] edge_trigger_i, 17 | 18 | // IRQ output for each context. 19 | output logic [NumContexts-1:0] irq_o, 20 | 21 | `TL_DECLARE_DEVICE_PORT(64, AddrWidth, SourceWidth, 1, link) 22 | ); 23 | 24 | logic bram_en; 25 | logic bram_we; 26 | logic [3:0] bram_wmask; 27 | logic [19:0] bram_addr; 28 | logic [31:0] bram_wrdata; 29 | logic [31:0] bram_rddata; 30 | 31 | plic #( 32 | .NUM_CONTEXTS (NumContexts), 33 | .NUM_IRQS (NumIrqs) 34 | ) plic ( 35 | .clk (clk_i), 36 | .rstn (rst_ni), 37 | .interrupts (interrupts_i), 38 | .edge_trigger (edge_trigger_i), 39 | .bram_addr ({bram_addr, 2'b0}), 40 | .bram_en (bram_en), 41 | .bram_we (bram_we ? &bram_wmask : 1'b0), 42 | .bram_rddata (bram_rddata), 43 | .bram_wrdata (bram_wrdata), 44 | .irq (irq_o) 45 | ); 46 | 47 | `TL_DECLARE(32, AddrWidth, SourceWidth, 1, narrow); 48 | 49 | tl_adapter_bram #( 50 | .DataWidth (32), 51 | .AddrWidth (AddrWidth), 52 | .SourceWidth (SourceWidth), 53 | .BramAddrWidth (20) 54 | ) bram_bridge ( 55 | .clk_i, 56 | .rst_ni, 57 | `TL_CONNECT_DEVICE_PORT(host, narrow), 58 | .bram_en_o (bram_en), 59 | .bram_we_o (bram_we), 60 | .bram_wmask_o (bram_wmask), 61 | .bram_addr_o (bram_addr), 62 | .bram_wdata_o (bram_wrdata), 63 | .bram_rdata_i (bram_rddata) 64 | ); 65 | 66 | tl_adapter #( 67 | .HostDataWidth (64), 68 | .DeviceDataWidth (32), 69 | .HostAddrWidth (AddrWidth), 70 | .DeviceAddrWidth (AddrWidth), 71 | .HostSourceWidth (SourceWidth), 72 | .DeviceSourceWidth (SourceWidth), 73 | .HostMaxSize (3), 74 | .DeviceMaxSize (2), 75 | .HostFifo (1'b0), 76 | .DeviceFifo (1'b1) 77 | ) narrower ( 78 | .clk_i, 79 | .rst_ni, 80 | `TL_FORWARD_DEVICE_PORT(host, link), 81 | `TL_CONNECT_HOST_PORT(device, narrow) 82 | ); 83 | 84 | endmodule 85 | -------------------------------------------------------------------------------- /firmware/src/block/mod.rs: -------------------------------------------------------------------------------- 1 | //! Block devices. 2 | //! 3 | //! This module provides a [`Block`] trait which bridges underlying block device implementation and 4 | //! I/O devices that behaves as HBAs. 5 | 6 | mod part; 7 | mod sd; 8 | pub use part::Part; 9 | pub use sd::Sd; 10 | 11 | use crate::io::Result; 12 | 13 | /// Capability description of a block device. 14 | #[non_exhaustive] 15 | pub struct Capability { 16 | /// Size of a block for this block device. 17 | pub blksize: usize, 18 | 19 | /// Whether discard operation is supported by the block device. 20 | pub discard: bool, 21 | } 22 | 23 | impl Default for Capability { 24 | fn default() -> Self { 25 | Capability { 26 | blksize: 512, 27 | discard: false, 28 | } 29 | } 30 | } 31 | 32 | /// Abstraction of a block device. 33 | pub trait Block { 34 | /// Reads the exact number of byte required to fill buf from the given offset. 35 | /// 36 | /// Caller must ensure `offset` and buffer size is aligned to `blksize` queried by `capability`. 37 | fn read_exact_at(&self, buf: &mut [u8], offset: u64) -> Result<()>; 38 | 39 | /// Attempts to write an entire buffer starting from a given offset. 40 | /// 41 | /// Caller must ensure `offset` and buffer size is aligned to `blksize` queried by `capability`. 42 | fn write_all_at(&self, buf: &[u8], offset: u64) -> Result<()>; 43 | 44 | /// Attempts to write zero to a given offset. 45 | /// 46 | /// Caller must ensure `offset` and `len` is aligned to `blksize` queried by `capability`. 47 | fn write_zero_at(&self, offset: u64, len: usize) -> Result<()> { 48 | let buf = vec![0; len]; 49 | self.write_all_at(&buf, offset) 50 | } 51 | 52 | /// Discard contents at the given offset. 53 | /// 54 | /// Caller must ensure `offset` and `len` is aligned to `blksize` queried by `capability`. 55 | fn discard(&self, offset: u64, len: usize) -> Result<()> { 56 | let _ = (offset, len); 57 | Ok(()) 58 | } 59 | 60 | /// Flush this block device. 61 | fn flush(&self) -> Result<()> { 62 | Ok(()) 63 | } 64 | 65 | /// Return the total size of this block device. 66 | fn len(&self) -> u64; 67 | 68 | /// Return the capability 69 | fn capability(&self) -> Capability { 70 | Default::default() 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /nexys_a7/rtl/muntjac_core_wrapper.sv: -------------------------------------------------------------------------------- 1 | `include "tl_util.svh" 2 | 3 | module muntjac_core_wrapper import muntjac_pkg::*; #( 4 | parameter DataWidth = 64, 5 | parameter PhysAddrLen = 56, 6 | parameter rv64f_e RV64F = RV64FNone, 7 | parameter int unsigned DCacheWaysWidth = 2, 8 | parameter int unsigned DCacheSetsWidth = 6, 9 | parameter int unsigned ICacheWaysWidth = 2, 10 | parameter int unsigned ICacheSetsWidth = 6, 11 | parameter int unsigned DTlbNumWays = 4, 12 | parameter int unsigned DTlbSetsWidth = 3, 13 | parameter int unsigned ITlbNumWays = 4, 14 | parameter int unsigned ITlbSetsWidth = 3, 15 | parameter int unsigned MHPMCounterNum = 0, 16 | parameter bit MHPMICacheEnable = 1'b0, 17 | parameter bit MHPMDCacheEnable = 1'b0, 18 | parameter int unsigned SourceWidth = 4, 19 | parameter int unsigned SinkWidth = 1 20 | ) ( 21 | // Clock and reset 22 | input logic clk_i, 23 | input logic rst_ni, 24 | 25 | // Memory interface 26 | `TL_DECLARE_HOST_PORT(DataWidth, PhysAddrLen, SourceWidth, SinkWidth, mem), 27 | 28 | input logic irq_software_m_i, 29 | input logic irq_timer_m_i, 30 | input logic irq_external_m_i, 31 | input logic irq_external_s_i, 32 | 33 | input logic [63:0] hart_id_i, 34 | 35 | input logic [HPM_EVENT_NUM-1:0] hpm_event_i, 36 | 37 | // Debug connections 38 | output instr_trace_t dbg_o 39 | ); 40 | 41 | muntjac_core #( 42 | .DataWidth (DataWidth), 43 | .PhysAddrLen (PhysAddrLen), 44 | .RV64F (RV64F), 45 | .DCacheWaysWidth (DCacheWaysWidth), 46 | .DCacheSetsWidth (DCacheSetsWidth), 47 | .ICacheWaysWidth (ICacheWaysWidth), 48 | .ICacheSetsWidth (ICacheSetsWidth), 49 | .DTlbNumWays (DTlbNumWays), 50 | .DTlbSetsWidth (DTlbSetsWidth), 51 | .ITlbNumWays (ITlbNumWays), 52 | .ITlbSetsWidth (ITlbSetsWidth), 53 | .MHPMCounterNum (MHPMCounterNum), 54 | .MHPMICacheEnable (MHPMICacheEnable), 55 | .MHPMDCacheEnable (MHPMDCacheEnable), 56 | .SourceWidth (SourceWidth), 57 | .SinkWidth (SinkWidth) 58 | ) core ( 59 | .clk_i, 60 | .rst_ni, 61 | `TL_FORWARD_HOST_PORT(mem, mem), 62 | .irq_software_m_i, 63 | .irq_timer_m_i, 64 | .irq_external_m_i, 65 | .irq_external_s_i, 66 | .hart_id_i, 67 | .hpm_event_i, 68 | .dbg_o 69 | ); 70 | 71 | endmodule 72 | -------------------------------------------------------------------------------- /nexys_video/rtl/muntjac_core_wrapper.sv: -------------------------------------------------------------------------------- 1 | `include "tl_util.svh" 2 | 3 | module muntjac_core_wrapper import muntjac_pkg::*; #( 4 | parameter DataWidth = 64, 5 | parameter PhysAddrLen = 56, 6 | parameter rv64f_e RV64F = RV64FNone, 7 | parameter int unsigned DCacheWaysWidth = 2, 8 | parameter int unsigned DCacheSetsWidth = 6, 9 | parameter int unsigned ICacheWaysWidth = 2, 10 | parameter int unsigned ICacheSetsWidth = 6, 11 | parameter int unsigned DTlbNumWays = 4, 12 | parameter int unsigned DTlbSetsWidth = 3, 13 | parameter int unsigned ITlbNumWays = 4, 14 | parameter int unsigned ITlbSetsWidth = 3, 15 | parameter int unsigned MHPMCounterNum = 0, 16 | parameter bit MHPMICacheEnable = 1'b0, 17 | parameter bit MHPMDCacheEnable = 1'b0, 18 | parameter int unsigned SourceWidth = 4, 19 | parameter int unsigned SinkWidth = 1 20 | ) ( 21 | // Clock and reset 22 | input logic clk_i, 23 | input logic rst_ni, 24 | 25 | // Memory interface 26 | `TL_DECLARE_HOST_PORT(DataWidth, PhysAddrLen, SourceWidth, SinkWidth, mem), 27 | 28 | input logic irq_software_m_i, 29 | input logic irq_timer_m_i, 30 | input logic irq_external_m_i, 31 | input logic irq_external_s_i, 32 | 33 | input logic [63:0] hart_id_i, 34 | 35 | input logic [HPM_EVENT_NUM-1:0] hpm_event_i, 36 | 37 | // Debug connections 38 | output instr_trace_t dbg_o 39 | ); 40 | 41 | muntjac_core #( 42 | .DataWidth (DataWidth), 43 | .PhysAddrLen (PhysAddrLen), 44 | .RV64F (RV64F), 45 | .DCacheWaysWidth (DCacheWaysWidth), 46 | .DCacheSetsWidth (DCacheSetsWidth), 47 | .ICacheWaysWidth (ICacheWaysWidth), 48 | .ICacheSetsWidth (ICacheSetsWidth), 49 | .DTlbNumWays (DTlbNumWays), 50 | .DTlbSetsWidth (DTlbSetsWidth), 51 | .ITlbNumWays (ITlbNumWays), 52 | .ITlbSetsWidth (ITlbSetsWidth), 53 | .MHPMCounterNum (MHPMCounterNum), 54 | .MHPMICacheEnable (MHPMICacheEnable), 55 | .MHPMDCacheEnable (MHPMDCacheEnable), 56 | .SourceWidth (SourceWidth), 57 | .SinkWidth (SinkWidth) 58 | ) core ( 59 | .clk_i, 60 | .rst_ni, 61 | `TL_FORWARD_HOST_PORT(mem, mem), 62 | .irq_software_m_i, 63 | .irq_timer_m_i, 64 | .irq_external_m_i, 65 | .irq_external_s_i, 66 | .hart_id_i, 67 | .hpm_event_i, 68 | .dbg_o 69 | ); 70 | 71 | endmodule 72 | -------------------------------------------------------------------------------- /firmware/src/util.rs: -------------------------------------------------------------------------------- 1 | use alloc::boxed::Box; 2 | use alloc::vec::Vec; 3 | use core::cell::UnsafeCell; 4 | use core::mem::{ManuallyDrop, MaybeUninit}; 5 | use core::ptr; 6 | use spin::Mutex; 7 | 8 | pub struct ScopeGuard { 9 | f: ManuallyDrop, 10 | } 11 | 12 | impl ScopeGuard { 13 | pub fn new(f: F) -> Self { 14 | Self { 15 | f: ManuallyDrop::new(f), 16 | } 17 | } 18 | } 19 | 20 | impl Drop for ScopeGuard { 21 | fn drop(&mut self) { 22 | unsafe { ManuallyDrop::take(&mut self.f)() } 23 | } 24 | } 25 | 26 | pub struct OnceCell { 27 | ready: Mutex, 28 | data: UnsafeCell>, 29 | } 30 | 31 | impl Drop for OnceCell { 32 | fn drop(&mut self) { 33 | if *self.ready.get_mut() { 34 | unsafe { 35 | ptr::drop_in_place((*self.data.get()).as_mut_ptr()); 36 | } 37 | } 38 | } 39 | } 40 | 41 | unsafe impl Send for OnceCell {} 42 | unsafe impl Sync for OnceCell {} 43 | 44 | impl OnceCell { 45 | pub const fn new() -> Self { 46 | OnceCell { 47 | ready: Mutex::new(false), 48 | data: UnsafeCell::new(MaybeUninit::uninit()), 49 | } 50 | } 51 | 52 | pub fn get_or_try_init(&self, f: F) -> Result<&T, E> 53 | where 54 | F: FnOnce() -> Result, 55 | { 56 | let mut guard = self.ready.lock(); 57 | if !*guard { 58 | let val = f()?; 59 | *guard = true; 60 | unsafe { ptr::write((*self.data.get()).as_mut_ptr(), val) }; 61 | } 62 | Ok(unsafe { &*(*self.data.get()).as_ptr() }) 63 | } 64 | } 65 | 66 | pub unsafe fn uninit_vec(len: usize) -> Vec { 67 | let mut vec = Vec::with_capacity(len); 68 | vec.set_len(len); 69 | vec 70 | } 71 | 72 | pub unsafe fn uninit_slice(len: usize) -> Box<[T]> { 73 | uninit_vec(len).into_boxed_slice() 74 | } 75 | 76 | pub unsafe fn uninit_array() -> Box<[T; N]> { 77 | uninit_slice(N).try_into().map_err(|_| ()).unwrap() 78 | } 79 | 80 | pub unsafe fn zeroed_slice(len: usize) -> Box<[T]> { 81 | let mut vec = uninit_slice(len); 82 | ptr::write_bytes(vec.as_mut_ptr(), 0, len); 83 | vec 84 | } 85 | 86 | pub fn view_as_u8_slice(obj: &T) -> &[u8] { 87 | unsafe { 88 | core::slice::from_raw_parts(obj as *const T as *const u8, core::mem::size_of_val(obj)) 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /util/create_rootfs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | if [ ! -f "/etc/debian_version" ]; then 6 | echo "E: The script can only be run on Debian based systems" 7 | exit 1 8 | fi 9 | 10 | if [ "$EUID" -ne 0 ]; then 11 | echo "I: The script can only be run as root, trying sudo" 12 | exec sudo /bin/bash "$0" "$@" 13 | fi 14 | 15 | echo "I: Checking for packages" 16 | 17 | # Check if necessary packages are installed 18 | REQUIRED_PKG="binfmt-support 19 | debian-archive-keyring 20 | file 21 | mmdebstrap 22 | qemu-user-static" 23 | INSTALLED_PKG=$(dpkg-query -W -f='${Package}\n' $REQUIRED_PKG 2>/dev/null | sort) 24 | MISSING_PKG=$(comm -23 <(echo "$REQUIRED_PKG") <(echo "$INSTALLED_PKG")) 25 | 26 | # Install missing packages 27 | if [ ! -z "$MISSING_PKG" ]; then 28 | echo "I: Missing packages $MISSING_PKG" 29 | read -p "Do you want to install them using apt? [y/N] " -n 1 -r 30 | echo 31 | if [[ $REPLY =~ ^[Yy]$ ]]; then 32 | apt-get install -y $MISSING_PKG 33 | else 34 | exit 1 35 | fi 36 | fi 37 | 38 | # Ensure the image already exists. Creating it as root will cause ownership issue. 39 | if [ ! -f "rootfs.img" ]; then 40 | echo "E: rootfs.img does not exist" 41 | fi 42 | 43 | # If the image is already formatted then warn before overwriting 44 | if file -s rootfs.img | grep -q "ext4 filesystem data"; then 45 | read -p "Do you want to overwrite it? [y/N] " -n 1 -r 46 | echo 47 | if [[ ! $REPLY =~ ^[Yy]$ ]]; then 48 | exit 1 49 | fi 50 | fi 51 | 52 | echo "I: Reserving space for the rootfs image" 53 | # Allocate 2GiB 54 | fallocate -l 2G rootfs.img || truncate -s 2G rootfs.img 55 | 56 | # Format the image with ext4 57 | echo "I: Formatting rootfs image with ext4" 58 | mkfs.ext4 -F rootfs.img 59 | 60 | # Create a temporary directory and mount the image there 61 | TMP_DIR=$(mktemp -d) 62 | tmp_cleanup() { 63 | rm -rf $TMP_DIR 64 | } 65 | trap tmp_cleanup EXIT 66 | 67 | echo "I: Mounting rootfs image to $TMP_DIR" 68 | mount rootfs.img $TMP_DIR 69 | mnt_cleanup() { 70 | echo "I: Unmounting rootfs image" 71 | umount $TMP_DIR 72 | tmp_cleanup 73 | } 74 | trap mnt_cleanup EXIT 75 | 76 | echo "I: Bootstrapping rootfs" 77 | mmdebstrap --architectures=riscv64 unstable $TMP_DIR http://deb.debian.org/debian/ 78 | 79 | echo "I: Updating apt sources" 80 | chroot $TMP_DIR apt-get update 81 | 82 | # Install additional packages 83 | echo "I: Install additional packages" 84 | chroot $TMP_DIR apt-get install -y $(cat data/debian_packages.txt) 85 | -------------------------------------------------------------------------------- /genesys_2/util/create_eth_ip.tcl: -------------------------------------------------------------------------------- 1 | # TODO: This requires a license to use. Replace this with an open source one. 2 | create_ip -name axi_ethernet -vendor xilinx.com -library ip -version 7.2 -module_name axi_ethernet_0 3 | set_property -dict [list \ 4 | CONFIG.PHY_TYPE {RGMII} \ 5 | CONFIG.TXCSUM {Full} \ 6 | CONFIG.RXCSUM {Full} \ 7 | CONFIG.Statistics_Counters {false} \ 8 | ] [get_ips axi_ethernet_0] 9 | 10 | # DMA needed for the AXI Ethernet IP 11 | create_ip -name axi_dma -vendor xilinx.com -library ip -version 7.1 -module_name axi_dma_eth 12 | set_property -dict [list \ 13 | CONFIG.Component_Name {axi_dma_eth} \ 14 | CONFIG.c_sg_length_width {16} \ 15 | CONFIG.c_m_axi_mm2s_data_width {64} \ 16 | CONFIG.c_include_mm2s_dre {1} \ 17 | CONFIG.c_mm2s_burst_size {256} \ 18 | CONFIG.c_sg_use_stsapp_length {1} \ 19 | CONFIG.c_m_axi_s2mm_data_width {64} \ 20 | CONFIG.c_include_s2mm_dre {1} \ 21 | CONFIG.c_s2mm_burst_size {256} \ 22 | ] [get_ips axi_dma_eth] 23 | 24 | # Clock wizard for ref/gtx clocks 25 | create_ip -name clk_wiz -vendor xilinx.com -library ip -version 6.0 -module_name clk_wiz_eth 26 | set_property -dict [list \ 27 | CONFIG.Component_Name {clk_wiz_eth} \ 28 | CONFIG.PRIMITIVE {PLL} \ 29 | CONFIG.PRIM_SOURCE {No_buffer} \ 30 | CONFIG.PRIM_IN_FREQ {50} \ 31 | CONFIG.CLKOUT2_USED {true} \ 32 | CONFIG.CLKOUT1_REQUESTED_OUT_FREQ {200} \ 33 | CONFIG.CLKOUT2_REQUESTED_OUT_FREQ {125} \ 34 | CONFIG.USE_LOCKED {false} \ 35 | CONFIG.USE_RESET {true} \ 36 | CONFIG.RESET_TYPE {ACTIVE_LOW} \ 37 | CONFIG.CLKIN1_JITTER_PS {200.0} \ 38 | CONFIG.CLKOUT1_DRIVES {BUFG} \ 39 | CONFIG.CLKOUT2_DRIVES {BUFG} \ 40 | CONFIG.CLKOUT3_DRIVES {BUFG} \ 41 | CONFIG.CLKOUT4_DRIVES {BUFG} \ 42 | CONFIG.CLKOUT5_DRIVES {BUFG} \ 43 | CONFIG.CLKOUT6_DRIVES {BUFG} \ 44 | CONFIG.CLKOUT7_DRIVES {BUFG} \ 45 | CONFIG.MMCM_DIVCLK_DIVIDE {1} \ 46 | CONFIG.MMCM_BANDWIDTH {OPTIMIZED} \ 47 | CONFIG.MMCM_CLKFBOUT_MULT_F {20} \ 48 | CONFIG.MMCM_CLKIN1_PERIOD {20.000} \ 49 | CONFIG.MMCM_CLKIN2_PERIOD {10.0} \ 50 | CONFIG.MMCM_COMPENSATION {ZHOLD} \ 51 | CONFIG.MMCM_CLKOUT0_DIVIDE_F {5} \ 52 | CONFIG.MMCM_CLKOUT1_DIVIDE {8} \ 53 | CONFIG.PLL_CLKIN_PERIOD {20.000} \ 54 | CONFIG.NUM_OUT_CLKS {2} \ 55 | CONFIG.RESET_PORT {resetn} \ 56 | CONFIG.CLKOUT1_JITTER {142.107} \ 57 | CONFIG.CLKOUT1_PHASE_ERROR {164.985} \ 58 | CONFIG.CLKOUT2_JITTER {154.207} \ 59 | CONFIG.CLKOUT2_PHASE_ERROR {164.985} \ 60 | ] [get_ips clk_wiz_eth] 61 | -------------------------------------------------------------------------------- /firmware/src/ethernet.rs: -------------------------------------------------------------------------------- 1 | pub fn mdio_read(addr: u8, reg: u8) -> u16 { 2 | unsafe { 3 | core::ptr::write_volatile(0x1004_07E4 as *mut u32, 0b1_00000_00000 | (addr as u32) << 5 | reg as u32); 4 | // Enable MDIO interface 5 | core::ptr::write_volatile(0x1004_07F0 as *mut u32, 0b1_00_0); 6 | // Initiate MDIO 7 | core::ptr::write_volatile(0x1004_07F0 as *mut u32, 0b1_00_1); 8 | // Wait for MDIO to complete 9 | while core::ptr::read_volatile(0x1004_07F0 as *mut u32) & 1 != 0 {} 10 | // Retrieve the value 11 | core::ptr::read_volatile(0x1004_07EC as *mut u32) as u16 12 | } 13 | } 14 | 15 | struct Xemaclite { 16 | base: usize, 17 | } 18 | 19 | impl Xemaclite { 20 | fn set_mac_address(&mut self, mac: [u8; 6]) { 21 | let w0 = u32::from_le_bytes([mac[0], mac[1], mac[2], mac[3]]); 22 | let w1 = u16::from_le_bytes([mac[4], mac[5]]) as u32; 23 | unsafe { 24 | // Put both addresses into the transmit buffer 25 | core::ptr::write_volatile((self.base + 0x0) as *mut u32, w0); 26 | core::ptr::write_volatile((self.base + 0x4) as *mut u32, w1); 27 | 28 | // Set both send and program bit to set MAC address 29 | core::ptr::write_volatile((self.base + 0x7FC) as *mut u32, 3); 30 | 31 | // Wait until status bit to turn low 32 | while core::ptr::read_volatile((self.base + 0x7FC) as *mut u32) & 1 != 0 {} 33 | } 34 | } 35 | } 36 | 37 | pub fn ethernet_init() { 38 | println!("Mac address setting"); 39 | Xemaclite { base: 0x1004_0000 }.set_mac_address([0x00, 0x00, 0x5E, 0x00, 0xFA, 0xCE]); 40 | println!("Mac address set"); 41 | println!("PHY ID = {}:{}", mdio_read(1, 2), mdio_read(1, 3)); 42 | let mut buffer = [0; 1520]; 43 | { 44 | while !recv(&mut buffer, false) {} 45 | dbg!(&buffer[6..12]); 46 | while !recv(&mut buffer, true) {} 47 | dbg!(&buffer[6..12]); 48 | } 49 | } 50 | 51 | pub fn recv(packet: &mut [u8], pong: bool) -> bool { 52 | let addr_offset = if pong { 0x1800 } else { 0x1000 }; 53 | unsafe { 54 | let status = core::ptr::read_volatile((0x1004_0000 + addr_offset + 0x7FC) as *mut u32); 55 | if status & 1 != 0 { 56 | for i in 0..380 { 57 | packet[i*4..i*4+4].copy_from_slice(&core::ptr::read_volatile((0x1004_0000 + addr_offset + i * 4) as *mut u32).to_le_bytes()); 58 | } 59 | core::ptr::write_volatile((0x1004_0000 + addr_offset + 0x7FC) as *mut u32, status &! 1); 60 | true 61 | } else { 62 | false 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /nexys_a7/util/create_eth_ip.tcl: -------------------------------------------------------------------------------- 1 | # TODO: This requires a license to use. Replace this with an open source one. 2 | create_ip -name axi_ethernet -vendor xilinx.com -library ip -version 7.2 -module_name axi_ethernet_0 3 | set_property -dict [list \ 4 | CONFIG.PHY_TYPE {MII} \ 5 | CONFIG.Include_IO {false} \ 6 | CONFIG.TXCSUM {Full} \ 7 | CONFIG.RXCSUM {Full} \ 8 | CONFIG.Statistics_Counters {false} \ 9 | ] [get_ips axi_ethernet_0] 10 | 11 | create_ip -name mii_to_rmii -vendor xilinx.com -library ip -version 2.0 -module_name mii_to_rmii_0 12 | set_property -dict [list CONFIG.C_FIXED_SPEED {1}] [get_ips mii_to_rmii_0] 13 | 14 | # DMA needed for the AXI Ethernet IP 15 | create_ip -name axi_dma -vendor xilinx.com -library ip -version 7.1 -module_name axi_dma_eth 16 | set_property -dict [list \ 17 | CONFIG.c_sg_length_width {16} \ 18 | CONFIG.c_m_axi_mm2s_data_width {64} \ 19 | CONFIG.c_include_mm2s_dre {1} \ 20 | CONFIG.c_mm2s_burst_size {256} \ 21 | CONFIG.c_sg_use_stsapp_length {1} \ 22 | CONFIG.c_m_axi_s2mm_data_width {64} \ 23 | CONFIG.c_include_s2mm_dre {1} \ 24 | CONFIG.c_s2mm_burst_size {256} \ 25 | ] [get_ips axi_dma_eth] 26 | 27 | # Clock wizard for ref/gtx clocks 28 | create_ip -name clk_wiz -vendor xilinx.com -library ip -version 6.0 -module_name clk_wiz_eth 29 | set_property -dict [list \ 30 | CONFIG.PRIMITIVE {PLL} \ 31 | CONFIG.PRIM_SOURCE {No_buffer} \ 32 | CONFIG.PRIM_IN_FREQ {50} \ 33 | CONFIG.CLKOUT2_USED {true} \ 34 | CONFIG.CLKOUT1_REQUESTED_OUT_FREQ {125} \ 35 | CONFIG.CLKOUT2_REQUESTED_OUT_FREQ {50} \ 36 | CONFIG.CLKOUT2_REQUESTED_PHASE {90.000} \ 37 | CONFIG.USE_LOCKED {false} \ 38 | CONFIG.USE_RESET {true} \ 39 | CONFIG.RESET_TYPE {ACTIVE_LOW} \ 40 | CONFIG.CLKIN1_JITTER_PS {200.0} \ 41 | CONFIG.CLKOUT1_DRIVES {BUFG} \ 42 | CONFIG.CLKOUT2_DRIVES {No_buffer} \ 43 | CONFIG.CLKOUT3_DRIVES {BUFG} \ 44 | CONFIG.CLKOUT4_DRIVES {BUFG} \ 45 | CONFIG.CLKOUT5_DRIVES {BUFG} \ 46 | CONFIG.CLKOUT6_DRIVES {BUFG} \ 47 | CONFIG.CLKOUT7_DRIVES {BUFG} \ 48 | CONFIG.MMCM_DIVCLK_DIVIDE {1} \ 49 | CONFIG.MMCM_BANDWIDTH {OPTIMIZED} \ 50 | CONFIG.MMCM_CLKFBOUT_MULT_F {20} \ 51 | CONFIG.MMCM_CLKIN1_PERIOD {20.000} \ 52 | CONFIG.MMCM_CLKIN2_PERIOD {10.0} \ 53 | CONFIG.MMCM_COMPENSATION {ZHOLD} \ 54 | CONFIG.MMCM_CLKOUT0_DIVIDE_F {8} \ 55 | CONFIG.MMCM_CLKOUT1_DIVIDE {20} \ 56 | CONFIG.MMCM_CLKOUT1_PHASE {90.000} \ 57 | CONFIG.PLL_CLKIN_PERIOD {20.000} \ 58 | CONFIG.NUM_OUT_CLKS {2} \ 59 | CONFIG.RESET_PORT {resetn} \ 60 | CONFIG.CLKOUT1_JITTER {154.207} \ 61 | CONFIG.CLKOUT1_PHASE_ERROR {164.985} \ 62 | CONFIG.CLKOUT2_JITTER {192.113} \ 63 | CONFIG.CLKOUT2_PHASE_ERROR {164.985} \ 64 | ] [get_ips clk_wiz_eth] 65 | -------------------------------------------------------------------------------- /firmware/src/memset.S: -------------------------------------------------------------------------------- 1 | .global memset 2 | .balign 16, 1 3 | .type memset, @function 4 | memset: 5 | /* Save for return value */ 6 | mv t6, a0 7 | 8 | /* 9 | * Register allocation for code below: 10 | * a0 - start of unfilled dst 11 | * t0 - end of unfilled dst 12 | */ 13 | add t0, a0, a2 14 | 15 | /* 16 | * Use bytewise fill if too small. 17 | * 18 | * This threshold must be at least 16 to ensure at least one wordwise fill 19 | * is performed. This will save at least 7 iterations of bytewise fill, 20 | * which pays off the fixed overhead. 21 | */ 22 | li a3, 16 23 | bltu a2, a3, .Lbyte_fill_tail 24 | 25 | /* 26 | * Bytewise fill first to align a0 to word boundary. 27 | */ 28 | addi a2, a0, 7 29 | andi a2, a2, ~7 30 | beq a0, a2, 2f 31 | 1: 32 | sb a1, 0(a0) 33 | addi a0, a0, 1 34 | bne a0, a2, 1b 35 | 2: 36 | 37 | /* Broadcast value into all bytes */ 38 | andi a1, a1, 0xff 39 | slli a3, a1, 8 40 | or a1, a3, a1 41 | slli a3, a1, 16 42 | or a1, a3, a1 43 | slli a3, a1, 32 44 | or a1, a3, a1 45 | 46 | /* Align the end pointer to word boundary */ 47 | andi a2, t0, ~7 48 | 49 | /* Calculate the offset into the loop */ 50 | sub a3, a2, a0 51 | neg a3, a3 52 | andi a3, a3, 32*8-1 53 | 54 | /* Offset the excessive increment in first iteration */ 55 | sub a0, a0, a3 56 | 57 | /* Jump into the loop by offset */ 58 | la a4, 1f 59 | srli a5, a3, 2 60 | add a4, a4, a5 61 | jr a4 62 | 63 | 1: 64 | c.sd a1, 0*8(a0) 65 | c.sd a1, 1*8(a0) 66 | c.sd a1, 2*8(a0) 67 | c.sd a1, 3*8(a0) 68 | c.sd a1, 4*8(a0) 69 | c.sd a1, 5*8(a0) 70 | c.sd a1, 6*8(a0) 71 | c.sd a1, 7*8(a0) 72 | c.sd a1, 8*8(a0) 73 | c.sd a1, 9*8(a0) 74 | c.sd a1, 10*8(a0) 75 | c.sd a1, 11*8(a0) 76 | c.sd a1, 12*8(a0) 77 | c.sd a1, 13*8(a0) 78 | c.sd a1, 14*8(a0) 79 | c.sd a1, 15*8(a0) 80 | c.sd a1, 16*8(a0) 81 | c.sd a1, 17*8(a0) 82 | c.sd a1, 18*8(a0) 83 | c.sd a1, 19*8(a0) 84 | c.sd a1, 20*8(a0) 85 | c.sd a1, 21*8(a0) 86 | c.sd a1, 22*8(a0) 87 | c.sd a1, 23*8(a0) 88 | c.sd a1, 24*8(a0) 89 | c.sd a1, 25*8(a0) 90 | c.sd a1, 26*8(a0) 91 | c.sd a1, 27*8(a0) 92 | c.sd a1, 28*8(a0) 93 | c.sd a1, 29*8(a0) 94 | c.sd a1, 30*8(a0) 95 | c.sd a1, 31*8(a0) 96 | addi a0, a0, 32*8 97 | bltu a0, a2, 1b 98 | 99 | .Lbyte_fill_tail: 100 | /* 101 | * Bytewise fill anything left. 102 | */ 103 | beq a0, t0, 2f 104 | 1: 105 | sb a1, 0(a0) 106 | addi a0, a0, 1 107 | bne a0, t0, 1b 108 | 2: 109 | 110 | mv a0, t6 111 | ret 112 | .size memset, . - memset 113 | -------------------------------------------------------------------------------- /rtl/sdhci.sv: -------------------------------------------------------------------------------- 1 | `include "axi_lite_util.svh" 2 | `include "tl_util.svh" 3 | 4 | module sdhci #( 5 | parameter AddrWidth = 12, 6 | parameter SourceWidth = 1 7 | ) ( 8 | // Clock and reset 9 | input clk_i, 10 | input rst_ni, 11 | input io_clk_i, 12 | 13 | // IO ports 14 | input sd_cd, 15 | inout sd_cmd, 16 | inout [3:0] sd_dat, 17 | output sd_reset, 18 | output sd_sck, 19 | 20 | // TileLink port 21 | `TL_DECLARE_DEVICE_PORT(32, AddrWidth, SourceWidth, 1, link), 22 | output irq_o 23 | ); 24 | 25 | logic sd_cmd_i; 26 | logic sd_cmd_o; 27 | logic sd_cmd_t; 28 | logic [3:0] sd_dat_i; 29 | logic [3:0] sd_dat_o; 30 | logic [3:0] sd_dat_t; 31 | 32 | IOBUF sd_cmd_iobuf ( 33 | .I (sd_cmd_o), 34 | .IO (sd_cmd), 35 | .O (sd_cmd_i), 36 | .T (sd_cmd_t) 37 | ); 38 | 39 | IOBUF sd_dat_iobuf0 ( 40 | .I (sd_dat_o[0]), 41 | .IO (sd_dat[0]), 42 | .O (sd_dat_i[0]), 43 | .T (sd_dat_t[0]) 44 | ); 45 | 46 | IOBUF sd_dat_iobuf1 ( 47 | .I (sd_dat_o[1]), 48 | .IO (sd_dat[1]), 49 | .O (sd_dat_i[1]), 50 | .T (sd_dat_t[1]) 51 | ); 52 | 53 | IOBUF sd_dat_iobuf2 ( 54 | .I (sd_dat_o[2]), 55 | .IO (sd_dat[2]), 56 | .O (sd_dat_i[2]), 57 | .T (sd_dat_t[2]) 58 | ); 59 | 60 | IOBUF sd_dat_iobuf3 ( 61 | .I (sd_dat_o[3]), 62 | .IO (sd_dat[3]), 63 | .O (sd_dat_i[3]), 64 | .T (sd_dat_t[3]) 65 | ); 66 | 67 | logic sdpower; 68 | assign sd_reset = ~sdpower; 69 | 70 | logic bram_en; 71 | logic bram_we; 72 | logic [3:0] bram_wmask; 73 | logic [5:0] bram_addr; 74 | logic [31:0] bram_wrdata; 75 | logic [31:0] bram_rddata; 76 | 77 | sd_host_intf sdhci ( 78 | .clk_i (clk_i), 79 | .rst_ni (rst_ni), 80 | .sd_base_clock (io_clk_i), 81 | .cmd_i (sd_cmd_i), 82 | .cmd_o (sd_cmd_o), 83 | .cmd_t (sd_cmd_t), 84 | .dat_i (sd_dat_i), 85 | .dat_o (sd_dat_o), 86 | .dat_t (sd_dat_t), 87 | .sdwp_ni (1'b1), 88 | .sdcd_ni (sd_cd), 89 | .sdpower_o (sdpower), 90 | .sdclk_o (sd_sck), 91 | .led_o (), 92 | .irq_o (irq_o), 93 | .wakeup_o (), 94 | .slot_irq_i ({7'd0, irq_o}), 95 | .bram_en (bram_en), 96 | .bram_we (bram_we ? bram_wmask : 4'd0), 97 | .bram_addr ({bram_addr, 2'b0}), 98 | .bram_wrdata (bram_wrdata), 99 | .bram_rddata (bram_rddata) 100 | ); 101 | 102 | tl_adapter_bram #( 103 | .DataWidth (32), 104 | .AddrWidth (AddrWidth), 105 | .SourceWidth (SourceWidth), 106 | .BramAddrWidth (6) 107 | ) bridge ( 108 | .clk_i (clk_i), 109 | .rst_ni (rst_ni), 110 | `TL_FORWARD_DEVICE_PORT(host, link), 111 | .bram_en_o (bram_en), 112 | .bram_we_o (bram_we), 113 | .bram_wmask_o (bram_wmask), 114 | .bram_addr_o (bram_addr), 115 | .bram_wdata_o (bram_wrdata), 116 | .bram_rdata_i (bram_rddata) 117 | ); 118 | 119 | endmodule 120 | -------------------------------------------------------------------------------- /util/program_bitstream.tcl: -------------------------------------------------------------------------------- 1 | if {[info exists env(PART)]} { 2 | set part $env(PART) 3 | } else { 4 | puts "ERROR: FPGA part not specified" 5 | exit 1 6 | } 7 | 8 | if {[info exists env(PROGRAM)]} { 9 | set program $env(PROGRAM) 10 | } else { 11 | puts "ERROR: MCS file not specified" 12 | exit 1 13 | } 14 | 15 | # Connect to Xilinx Hardware Server 16 | if { [ catch { open_hw_manager } ] } { open_hw } 17 | 18 | if {[info exists env(HW_SERVER)]} { 19 | connect_hw_server -url $env(HW_SERVER) -allow_non_jtag 20 | } else { 21 | connect_hw_server 22 | } 23 | 24 | set hw_targets [get_hw_targets] 25 | 26 | if { [llength $hw_targets] == 0 } { 27 | puts "ERROR: Failed to find any targets" 28 | exit 1 29 | } 30 | 31 | # Find the first target and device that contains a FPGA $part. 32 | set hw_device_found 0 33 | foreach hw_target $hw_targets { 34 | puts "NFO: Trying to use hardware target $hw_target" 35 | current_hw_target $hw_target 36 | 37 | # Open hardware target 38 | # The Vivado hardware server isn't always able to reliably open a target. 39 | # Try three times before giving up. 40 | set hw_target_opened 0 41 | for {set open_hw_target_try 1} {$open_hw_target_try <= 3} {incr open_hw_target_try} { 42 | if {[catch {open_hw_target} res_open_hw_target] == 0} { 43 | set hw_target_opened 1 44 | break 45 | } 46 | } 47 | if { $hw_target_opened == 0 } { 48 | puts "WARNING: Unable to open hardware target $hw_target after " \ 49 | "$open_hw_target_try tries. Skipping." 50 | continue 51 | } 52 | puts "INFO: Opened hardware target $hw_target on try $open_hw_target_try." 53 | 54 | # Iterate through all devices and find one which contains $part 55 | foreach { hw_device } [get_hw_devices] { 56 | if { [string first [get_property PART $hw_device] $part] == 0 } { 57 | puts "INFO: Found $part as part of $hw_device." 58 | current_hw_device $hw_device 59 | set hw_device_found 1 60 | break 61 | } 62 | } 63 | 64 | if { $hw_device_found == 1 } { 65 | break 66 | } else { 67 | # Close currently tried device, and try with next one. 68 | puts "INFO: Part not found as part of $hw_target. Trying next device." 69 | close_hw_target 70 | } 71 | } 72 | if { $hw_device_found == 0 } { 73 | puts "ERROR: None of the hardware targets included a $part FPGA part. \ 74 | Check cables and ensure that jumpers are correct for JTAG programming." 75 | exit 1 76 | } 77 | puts "INFO: Programming bitstream to device $hw_device on target $hw_target." 78 | 79 | current_hw_device $hw_device 80 | set_property PARAM.FREQUENCY 15000000 [get_hw_targets $hw_target] 81 | 82 | set_property PROGRAM.FILE [list $program] [current_hw_device] 83 | program_hw_devices [current_hw_device] 84 | 85 | # Disconnect from Xilinx Hardware Server 86 | close_hw_target 87 | disconnect_hw_server 88 | 89 | puts "" 90 | puts "INFO: SUCCESS! FPGA has been programmed with bitstream $program" 91 | -------------------------------------------------------------------------------- /genesys_2/Makefile: -------------------------------------------------------------------------------- 1 | LD_VERSION := $(shell riscv64-unknown-linux-gnu-ld -v 2>/dev/null) 2 | 3 | ifdef LD_VERSION 4 | PREFIX = riscv64-unknown-linux-gnu- 5 | else 6 | PREFIX = riscv64-linux-gnu- 7 | endif 8 | 9 | default: firmware 10 | 11 | .PHONY: default firmware project bitstream program-flash-firmware program-flash 12 | 13 | FORCE: 14 | 15 | ../build/linker: FORCE 16 | @$(MAKE) -C .. -q build/linker || $(MAKE) -C .. build/linker 17 | 18 | CARGO_OUT_DIR=$(realpath .)/build/riscv64imac-unknown-none-elf/release 19 | 20 | device_tree.dts: 21 | cp data/device_tree.dts . 22 | 23 | # Files colleted by Cargo 24 | -include build/firmware.d 25 | 26 | firmware.elf: ../build/linker device_tree.dts 27 | cd ../firmware; CC=$(PREFIX)gcc CFLAGS="-fno-pic" CARGO_TARGET_DIR=$(abspath ./build) DTS=$(realpath device_tree.dts) cargo build --release 28 | awk '{split($$0,a,": ");print "firmware.elf:"a[2];n=split(a[2],b," ");for(i=0;++i<=n;)print b[i]":"}' $(CARGO_OUT_DIR)/bootloader.d > build/firmware.d 29 | cp $(CARGO_OUT_DIR)/bootloader $@ 30 | 31 | firmware.bin: firmware.elf 32 | $(PREFIX)objcopy $< $@ -O binary 33 | 34 | firmware.mcs: firmware.bin 35 | vivado -notrace -mode batch -source util/generate_mcs.tcl -tclargs nobit 36 | 37 | firmware: firmware.elf 38 | 39 | PROJECT_DIR=build/garyguo.net_systems_muntjac_soc_0.1/genesys_2-vivado 40 | PROJECT_NAME=garyguo.net_systems_muntjac_soc_0.1 41 | PROJECT=$(PROJECT_DIR)/$(PROJECT_NAME).xpr 42 | 43 | # Files collected by FuseSoC 44 | fusesoc-deps= 45 | -include $(PROJECT_DIR)/core-deps.mk 46 | 47 | # If any file is missing then force a rebuild. 48 | fusesoc-deps:=$(foreach f,$(fusesoc-deps),$(if $(wildcard $f),$f,FORCE)) 49 | 50 | # Vivado can handle changed SystemVerilog files, so filter them out 51 | # TODO: How about generated files 52 | fusesoc-deps-sv=$(filter %.sv %.svh %.xdc,$(fusesoc-deps)) 53 | fusesoc-deps-no-sv=$(filter-out %.sv %.svh %.xdc,$(fusesoc-deps)) 54 | 55 | project: $(PROJECT) 56 | 57 | $(PROJECT): $(fusesoc-deps-no-sv) 58 | fusesoc --cores-root=.. run --setup --target=genesys_2 --no-export garyguo.net:systems:muntjac_soc 59 | $(MAKE) -C $(PROJECT_DIR) $(PROJECT_NAME).xpr 60 | 61 | bitstream: bitstream.bit 62 | 63 | $(PROJECT_DIR)/$(PROJECT_NAME).bit: $(PROJECT) $(fusesoc-deps-sv) 64 | cd $(PROJECT_DIR); vivado -notrace -mode batch -source $(realpath ../util/generate_bitstream.tcl) $(PROJECT_NAME).xpr 65 | 66 | bitstream.bit: $(PROJECT_DIR)/$(PROJECT_NAME).bit 67 | cp $< $@ 68 | 69 | bitstream.mcs: firmware.bin bitstream.bit 70 | vivado -notrace -mode batch -source util/generate_mcs.tcl -tclargs bit 71 | 72 | program-flash-firmware: firmware.mcs 73 | PART="xc7k325tffg900-2" CFGMEM_PART="s25fl256sxxxxxx0-spi-x1_x2_x4" PROGRAM="$<" vivado -notrace -mode batch -source ../util/program_flash.tcl 74 | 75 | program-flash: bitstream.mcs 76 | PART="xc7k325tffg900-2" CFGMEM_PART="s25fl256sxxxxxx0-spi-x1_x2_x4" PROGRAM="$<" vivado -notrace -mode batch -source ../util/program_flash.tcl 77 | 78 | program-bitstream: bitstream.bit 79 | PART="xc7k325tffg900-2" PROGRAM="$<" vivado -notrace -mode batch -source ../util/program_bitstream.tcl 80 | 81 | clean: 82 | rm -rf build 83 | -------------------------------------------------------------------------------- /nexys_a7/Makefile: -------------------------------------------------------------------------------- 1 | LD_VERSION := $(shell riscv64-unknown-linux-gnu-ld -v 2>/dev/null) 2 | 3 | ifdef LD_VERSION 4 | PREFIX = riscv64-unknown-linux-gnu- 5 | else 6 | PREFIX = riscv64-linux-gnu- 7 | endif 8 | 9 | default: firmware 10 | 11 | .PHONY: default firmware project bitstream program-flash-firmware program-flash 12 | 13 | FORCE: 14 | 15 | ../build/linker: FORCE 16 | @$(MAKE) -C .. -q build/linker || $(MAKE) -C .. build/linker 17 | 18 | CARGO_OUT_DIR=$(realpath .)/build/riscv64imac-unknown-none-elf/release 19 | 20 | device_tree.dts: 21 | cp data/device_tree.dts . 22 | 23 | # Files colleted by Cargo 24 | -include build/firmware.d 25 | 26 | firmware.elf: ../build/linker device_tree.dts 27 | cd ../firmware; CC=$(PREFIX)gcc CFLAGS="-fno-pic" CARGO_TARGET_DIR=$(abspath ./build) DTS=$(realpath device_tree.dts) cargo build --release 28 | awk '{split($$0,a,": ");print "firmware.elf:"a[2];n=split(a[2],b," ");for(i=0;++i<=n;)print b[i]":"}' $(CARGO_OUT_DIR)/bootloader.d > build/firmware.d 29 | cp $(CARGO_OUT_DIR)/bootloader $@ 30 | 31 | firmware.bin: firmware.elf 32 | $(PREFIX)objcopy $< $@ -O binary 33 | 34 | firmware.mcs: firmware.bin 35 | vivado -notrace -mode batch -source util/generate_mcs.tcl -tclargs nobit 36 | 37 | firmware: firmware.elf 38 | 39 | PROJECT_DIR=build/garyguo.net_systems_muntjac_soc_0.1/nexys_a7-vivado 40 | PROJECT_NAME=garyguo.net_systems_muntjac_soc_0.1 41 | PROJECT=$(PROJECT_DIR)/$(PROJECT_NAME).xpr 42 | 43 | # Files collected by FuseSoC 44 | fusesoc-deps= 45 | -include $(PROJECT_DIR)/core-deps.mk 46 | 47 | # If any file is missing then force a rebuild. 48 | fusesoc-deps:=$(foreach f,$(fusesoc-deps),$(if $(wildcard $f),$f,FORCE)) 49 | 50 | # Vivado can handle changed SystemVerilog files, so filter them out 51 | # TODO: How about generated files 52 | fusesoc-deps-sv=$(filter %.sv %.svh %.xdc,$(fusesoc-deps)) 53 | fusesoc-deps-no-sv=$(filter-out %.sv %.svh %.xdc,$(fusesoc-deps)) 54 | 55 | project: $(PROJECT) 56 | 57 | $(PROJECT): $(fusesoc-deps-no-sv) 58 | fusesoc --cores-root=.. run --setup --target=nexys_a7 --no-export garyguo.net:systems:muntjac_soc 59 | $(MAKE) -C $(PROJECT_DIR) $(PROJECT_NAME).xpr 60 | 61 | bitstream: bitstream.bit 62 | 63 | $(PROJECT_DIR)/$(PROJECT_NAME).bit: $(PROJECT) $(fusesoc-deps-sv) 64 | cd $(PROJECT_DIR); vivado -notrace -mode batch -source $(realpath ../util/generate_bitstream.tcl) $(PROJECT_NAME).xpr 65 | 66 | bitstream.bit: $(PROJECT_DIR)/$(PROJECT_NAME).bit 67 | cp $< $@ 68 | 69 | bitstream.mcs: firmware.bin bitstream.bit 70 | vivado -notrace -mode batch -source util/generate_mcs.tcl -tclargs bit 71 | 72 | program-flash-firmware: firmware.mcs 73 | PART="xc7a100tcsg324-1" CFGMEM_PART="s25fl128sxxxxxx0-spi-x1_x2_x4" PROGRAM="$<" vivado -notrace -mode batch -source ../util/program_flash.tcl 74 | 75 | program-flash: bitstream.mcs 76 | PART="xc7a100tcsg324-1" CFGMEM_PART="s25fl128sxxxxxx0-spi-x1_x2_x4" PROGRAM="$<" vivado -notrace -mode batch -source ../util/program_flash.tcl 77 | 78 | program-bitstream: bitstream.bit 79 | PART="xc7a100tcsg324-1" PROGRAM="$<" vivado -notrace -mode batch -source ../util/program_bitstream.tcl 80 | 81 | clean: 82 | rm -rf build 83 | -------------------------------------------------------------------------------- /nexys_video/Makefile: -------------------------------------------------------------------------------- 1 | LD_VERSION := $(shell riscv64-unknown-linux-gnu-ld -v 2>/dev/null) 2 | 3 | ifdef LD_VERSION 4 | PREFIX = riscv64-unknown-linux-gnu- 5 | else 6 | PREFIX = riscv64-linux-gnu- 7 | endif 8 | 9 | default: firmware 10 | 11 | .PHONY: default firmware project bitstream program-flash-firmware program-flash 12 | 13 | FORCE: 14 | 15 | ../build/linker: FORCE 16 | @$(MAKE) -C .. -q build/linker || $(MAKE) -C .. build/linker 17 | 18 | CARGO_OUT_DIR=$(realpath .)/build/riscv64imac-unknown-none-elf/release 19 | 20 | device_tree.dts: 21 | cp data/device_tree.dts . 22 | 23 | # Files colleted by Cargo 24 | -include build/firmware.d 25 | 26 | firmware.elf: ../build/linker device_tree.dts 27 | cd ../firmware; CC=$(PREFIX)gcc CFLAGS="-fno-pic" CARGO_TARGET_DIR=$(abspath ./build) DTS=$(realpath device_tree.dts) cargo build --release 28 | awk '{split($$0,a,": ");print "firmware.elf:"a[2];n=split(a[2],b," ");for(i=0;++i<=n;)print b[i]":"}' $(CARGO_OUT_DIR)/bootloader.d > build/firmware.d 29 | cp $(CARGO_OUT_DIR)/bootloader $@ 30 | 31 | firmware.bin: firmware.elf 32 | $(PREFIX)objcopy $< $@ -O binary 33 | 34 | firmware.mcs: firmware.bin 35 | vivado -notrace -mode batch -source util/generate_mcs.tcl -tclargs nobit 36 | 37 | firmware: firmware.elf 38 | 39 | PROJECT_DIR=build/garyguo.net_systems_muntjac_soc_0.1/nexys_video-vivado 40 | PROJECT_NAME=garyguo.net_systems_muntjac_soc_0.1 41 | PROJECT=$(PROJECT_DIR)/$(PROJECT_NAME).xpr 42 | 43 | # Files collected by FuseSoC 44 | fusesoc-deps= 45 | -include $(PROJECT_DIR)/core-deps.mk 46 | 47 | # If any file is missing then force a rebuild. 48 | fusesoc-deps:=$(foreach f,$(fusesoc-deps),$(if $(wildcard $f),$f,FORCE)) 49 | 50 | # Vivado can handle changed SystemVerilog files, so filter them out 51 | # TODO: How about generated files 52 | fusesoc-deps-sv=$(filter %.sv %.svh %.xdc,$(fusesoc-deps)) 53 | fusesoc-deps-no-sv=$(filter-out %.sv %.svh %.xdc,$(fusesoc-deps)) 54 | 55 | project: $(PROJECT) 56 | 57 | $(PROJECT): $(fusesoc-deps-no-sv) 58 | fusesoc --cores-root=.. run --setup --target=nexys_video --no-export garyguo.net:systems:muntjac_soc 59 | $(MAKE) -C $(PROJECT_DIR) $(PROJECT_NAME).xpr 60 | 61 | bitstream: bitstream.bit 62 | 63 | $(PROJECT_DIR)/$(PROJECT_NAME).bit: $(PROJECT) $(fusesoc-deps-sv) 64 | cd $(PROJECT_DIR); vivado -notrace -mode batch -source $(realpath ../util/generate_bitstream.tcl) $(PROJECT_NAME).xpr 65 | 66 | bitstream.bit: $(PROJECT_DIR)/$(PROJECT_NAME).bit 67 | cp $< $@ 68 | 69 | bitstream.mcs: firmware.bin bitstream.bit 70 | vivado -notrace -mode batch -source util/generate_mcs.tcl -tclargs bit 71 | 72 | program-flash-firmware: firmware.mcs 73 | PART="xc7a200tsbg484-1" CFGMEM_PART="s25fl256sxxxxxx0-spi-x1_x2_x4" PROGRAM="$<" vivado -notrace -mode batch -source ../util/program_flash.tcl 74 | 75 | program-flash: bitstream.mcs 76 | PART="xc7a200tsbg484-1" CFGMEM_PART="s25fl256sxxxxxx0-spi-x1_x2_x4" PROGRAM="$<" vivado -notrace -mode batch -source ../util/program_flash.tcl 77 | 78 | program-bitstream: bitstream.bit 79 | PART="xc7a200tsbg484-1" PROGRAM="$<" vivado -notrace -mode batch -source ../util/program_bitstream.tcl 80 | 81 | clean: 82 | rm -rf build 83 | -------------------------------------------------------------------------------- /firmware/src/misalign.rs: -------------------------------------------------------------------------------- 1 | use core::arch::asm; 2 | use riscv::Op; 3 | 4 | use super::memory; 5 | use super::{Context, TrapInfo}; 6 | 7 | fn load_instruction(pc: usize) -> (u32, Op) { 8 | let bits_lo = memory::load_u16_exec(pc).unwrap(); 9 | let (bits, insn) = if bits_lo & 3 != 3 { 10 | (bits_lo as u32, riscv::decode_compressed(bits_lo)) 11 | } else { 12 | let bits = bits_lo as u32 | ((memory::load_u16_exec(pc + 2).unwrap() as u32) << 16); 13 | (bits, riscv::decode(bits)) 14 | }; 15 | (bits, insn) 16 | } 17 | 18 | pub fn handle_misaligned_read(ctx: &mut Context) -> Result<(), TrapInfo> { 19 | let addr; 20 | unsafe { 21 | asm!("csrr {}, mtval", lateout(reg) addr, options(nomem, nostack)); 22 | } 23 | let (bits, insn) = load_instruction(ctx.pc); 24 | 25 | match insn { 26 | Op::Lh { rd, .. } => { 27 | let mut bytes = [0; 2]; 28 | memory::load(&mut bytes, addr)?; 29 | ctx.registers[rd as usize] = i16::from_le_bytes(bytes) as usize; 30 | } 31 | Op::Lw { rd, .. } => { 32 | let mut bytes = [0; 4]; 33 | memory::load(&mut bytes, addr)?; 34 | ctx.registers[rd as usize] = i32::from_le_bytes(bytes) as usize; 35 | } 36 | Op::Ld { rd, .. } => { 37 | let mut bytes = [0; 8]; 38 | memory::load(&mut bytes, addr)?; 39 | ctx.registers[rd as usize] = u64::from_le_bytes(bytes) as usize; 40 | } 41 | Op::Lhu { rd, .. } => { 42 | let mut bytes = [0; 2]; 43 | memory::load(&mut bytes, addr)?; 44 | ctx.registers[rd as usize] = u16::from_le_bytes(bytes) as usize; 45 | } 46 | Op::Lwu { rd, .. } => { 47 | let mut bytes = [0; 4]; 48 | memory::load(&mut bytes, addr)?; 49 | ctx.registers[rd as usize] = u32::from_le_bytes(bytes) as usize; 50 | } 51 | _ => panic!("unexpected misaligned read: {}", insn), 52 | } 53 | 54 | ctx.pc += if bits & 3 == 3 { 4 } else { 2 }; 55 | Ok(()) 56 | } 57 | 58 | pub fn handle_misaligned_write(ctx: &mut Context) -> Result<(), TrapInfo> { 59 | let addr; 60 | unsafe { 61 | asm!("csrr {}, mtval", lateout(reg) addr, options(nomem, nostack)); 62 | } 63 | let (bits, insn) = load_instruction(ctx.pc); 64 | 65 | match insn { 66 | Op::Sh { rs2, .. } => { 67 | let bytes = (ctx.registers[rs2 as usize] as u16).to_le_bytes(); 68 | memory::store(addr, &bytes)?; 69 | } 70 | Op::Sw { rs2, .. } => { 71 | let bytes = (ctx.registers[rs2 as usize] as u32).to_le_bytes(); 72 | memory::store(addr, &bytes)?; 73 | } 74 | Op::Sd { rs2, .. } => { 75 | let bytes = ctx.registers[rs2 as usize].to_le_bytes(); 76 | memory::store(addr, &bytes)?; 77 | } 78 | _ => { 79 | return Err(TrapInfo { 80 | cause: 6, 81 | tval: addr, 82 | }) 83 | } 84 | } 85 | 86 | ctx.pc += if bits & 3 == 3 { 4 } else { 2 }; 87 | Ok(()) 88 | } 89 | -------------------------------------------------------------------------------- /genesys_2/rtl/dvi.sv: -------------------------------------------------------------------------------- 1 | `include "axi_util.svh" 2 | `include "tl_util.svh" 3 | 4 | module dvi #( 5 | parameter IoAddrWidth = 30, 6 | parameter IoSourceWidth = 5, 7 | parameter DmaDataWidth = 64, 8 | parameter DmaAddrWidth = 64, 9 | parameter DmaSourceWidth = 2, 10 | parameter DmaSinkWidth = 1 11 | ) ( 12 | // Clock and reset 13 | input clk_i, 14 | input rst_ni, 15 | input io_clk_i, 16 | 17 | // IO port 18 | output hdmi_tx_clk_p, 19 | output hdmi_tx_clk_n, 20 | output [2:0] hdmi_tx_p, 21 | output [2:0] hdmi_tx_n, 22 | 23 | // TileLink port 24 | `TL_DECLARE_DEVICE_PORT(32, IoAddrWidth, IoSourceWidth, 1, io), 25 | `TL_DECLARE_HOST_PORT(DmaDataWidth, DmaAddrWidth, DmaSourceWidth, DmaSinkWidth, dma) 26 | ); 27 | 28 | // Clock wizard 29 | 30 | wire pxl_clk; 31 | wire pxl_clk_5; 32 | clk_wiz_dvi clk_wiz ( 33 | .clk_in1 (io_clk_i), 34 | .resetn (rst_ni), 35 | .clk_out1 (pxl_clk), 36 | .clk_out2 (pxl_clk_5) 37 | ); 38 | 39 | logic [7:0] red; 40 | logic [7:0] green; 41 | logic [7:0] blue; 42 | logic pixel; 43 | logic hsync; 44 | logic vsync; 45 | 46 | logic bram_en; 47 | logic bram_we; 48 | logic [3:0] bram_wmask; 49 | logic [5:0] bram_addr; 50 | logic [31:0] bram_wrdata; 51 | logic [31:0] bram_rddata; 52 | 53 | `TL_DECLARE(DmaDataWidth, DmaAddrWidth, DmaSourceWidth, 1, axi); 54 | 55 | display_controller #( 56 | .DataWidth (DmaDataWidth), 57 | .AddrWidth (DmaAddrWidth), 58 | .SourceWidth (DmaSourceWidth) 59 | ) display ( 60 | .clk_i (clk_i), 61 | .rst_ni (rst_ni), 62 | .pxl_clk_freq_o (), 63 | .pxl_clk_i (pxl_clk), 64 | .pxl_clk_en_i (1'b1), 65 | .red_o (red), 66 | .green_o (green), 67 | .blue_o (blue), 68 | .pixel_o (pixel), 69 | .hsync_o (hsync), 70 | .vsync_o (vsync), 71 | .ctrl_en_i (bram_en), 72 | .ctrl_we_i (|bram_we), 73 | .ctrl_addr_i ({bram_addr, 2'b0}), 74 | .ctrl_wrdata_i (bram_wrdata), 75 | .ctrl_rddata_o (bram_rddata), 76 | `TL_CONNECT_HOST_PORT(dma, axi) 77 | ); 78 | 79 | tl_adapter_bram #( 80 | .DataWidth (32), 81 | .AddrWidth (IoAddrWidth), 82 | .SourceWidth (IoSourceWidth), 83 | .BramAddrWidth (6) 84 | ) bridge ( 85 | .clk_i (clk_i), 86 | .rst_ni (rst_ni), 87 | `TL_FORWARD_DEVICE_PORT(host, io), 88 | .bram_en_o (bram_en), 89 | .bram_we_o (bram_we), 90 | .bram_wmask_o (bram_wmask), 91 | .bram_addr_o (bram_addr), 92 | .bram_wdata_o (bram_wrdata), 93 | .bram_rdata_i (bram_rddata) 94 | ); 95 | 96 | tl_adapter #( 97 | .DataWidth (DmaDataWidth), 98 | .AddrWidth (DmaAddrWidth), 99 | .SourceWidth (DmaSourceWidth), 100 | .HostSinkWidth (1), 101 | .DeviceSinkWidth (DmaSinkWidth) 102 | ) dma_bridge ( 103 | .clk_i, 104 | .rst_ni, 105 | `TL_CONNECT_DEVICE_PORT(host, axi), 106 | `TL_FORWARD_HOST_PORT(device, dma) 107 | ); 108 | 109 | rgb2dvi_0 rgb2dvi ( 110 | .vid_pData ({red, blue, green}), 111 | .vid_pHSync (hsync), 112 | .vid_pVSync (vsync), 113 | .vid_pVDE (pixel), 114 | .aRst_n (rst_ni), 115 | .PixelClk (pxl_clk), 116 | .SerialClk (pxl_clk_5), 117 | .TMDS_Clk_p (hdmi_tx_clk_p), 118 | .TMDS_Clk_n (hdmi_tx_clk_n), 119 | .TMDS_Data_p (hdmi_tx_p), 120 | .TMDS_Data_n (hdmi_tx_n) 121 | ); 122 | 123 | endmodule 124 | -------------------------------------------------------------------------------- /nexys_video/rtl/dvi.sv: -------------------------------------------------------------------------------- 1 | `include "axi_util.svh" 2 | `include "tl_util.svh" 3 | 4 | module dvi #( 5 | parameter IoAddrWidth = 30, 6 | parameter IoSourceWidth = 5, 7 | parameter DmaDataWidth = 64, 8 | parameter DmaAddrWidth = 64, 9 | parameter DmaSourceWidth = 2, 10 | parameter DmaSinkWidth = 1 11 | ) ( 12 | // Clock and reset 13 | input clk_i, 14 | input rst_ni, 15 | input io_clk_i, 16 | 17 | // IO port 18 | output hdmi_tx_clk_p, 19 | output hdmi_tx_clk_n, 20 | output [2:0] hdmi_tx_p, 21 | output [2:0] hdmi_tx_n, 22 | 23 | // TileLink port 24 | `TL_DECLARE_DEVICE_PORT(32, IoAddrWidth, IoSourceWidth, 1, io), 25 | `TL_DECLARE_HOST_PORT(DmaDataWidth, DmaAddrWidth, DmaSourceWidth, DmaSinkWidth, dma) 26 | ); 27 | 28 | // Clock wizard 29 | 30 | wire pxl_clk; 31 | wire pxl_clk_5; 32 | clk_wiz_dvi clk_wiz ( 33 | .clk_in1 (io_clk_i), 34 | .resetn (rst_ni), 35 | .clk_out1 (pxl_clk), 36 | .clk_out2 (pxl_clk_5) 37 | ); 38 | 39 | logic [7:0] red; 40 | logic [7:0] green; 41 | logic [7:0] blue; 42 | logic pixel; 43 | logic hsync; 44 | logic vsync; 45 | 46 | logic bram_en; 47 | logic bram_we; 48 | logic [3:0] bram_wmask; 49 | logic [5:0] bram_addr; 50 | logic [31:0] bram_wrdata; 51 | logic [31:0] bram_rddata; 52 | 53 | `TL_DECLARE(DmaDataWidth, DmaAddrWidth, DmaSourceWidth, 1, axi); 54 | 55 | display_controller #( 56 | .DataWidth (DmaDataWidth), 57 | .AddrWidth (DmaAddrWidth), 58 | .SourceWidth (DmaSourceWidth) 59 | ) display ( 60 | .clk_i (clk_i), 61 | .rst_ni (rst_ni), 62 | .pxl_clk_freq_o (), 63 | .pxl_clk_i (pxl_clk), 64 | .pxl_clk_en_i (1'b1), 65 | .red_o (red), 66 | .green_o (green), 67 | .blue_o (blue), 68 | .pixel_o (pixel), 69 | .hsync_o (hsync), 70 | .vsync_o (vsync), 71 | .ctrl_en_i (bram_en), 72 | .ctrl_we_i (|bram_we), 73 | .ctrl_addr_i ({bram_addr, 2'b0}), 74 | .ctrl_wrdata_i (bram_wrdata), 75 | .ctrl_rddata_o (bram_rddata), 76 | `TL_CONNECT_HOST_PORT(dma, axi) 77 | ); 78 | 79 | tl_adapter_bram #( 80 | .DataWidth (32), 81 | .AddrWidth (IoAddrWidth), 82 | .SourceWidth (IoSourceWidth), 83 | .BramAddrWidth (6) 84 | ) bridge ( 85 | .clk_i (clk_i), 86 | .rst_ni (rst_ni), 87 | `TL_FORWARD_DEVICE_PORT(host, io), 88 | .bram_en_o (bram_en), 89 | .bram_we_o (bram_we), 90 | .bram_wmask_o (bram_wmask), 91 | .bram_addr_o (bram_addr), 92 | .bram_wdata_o (bram_wrdata), 93 | .bram_rdata_i (bram_rddata) 94 | ); 95 | 96 | tl_adapter #( 97 | .DataWidth (DmaDataWidth), 98 | .AddrWidth (DmaAddrWidth), 99 | .SourceWidth (DmaSourceWidth), 100 | .HostSinkWidth (1), 101 | .DeviceSinkWidth (DmaSinkWidth) 102 | ) dma_bridge ( 103 | .clk_i, 104 | .rst_ni, 105 | `TL_CONNECT_DEVICE_PORT(host, axi), 106 | `TL_FORWARD_HOST_PORT(device, dma) 107 | ); 108 | 109 | rgb2dvi_0 rgb2dvi ( 110 | .vid_pData ({red, blue, green}), 111 | .vid_pHSync (hsync), 112 | .vid_pVSync (vsync), 113 | .vid_pVDE (pixel), 114 | .aRst_n (rst_ni), 115 | .PixelClk (pxl_clk), 116 | .SerialClk (pxl_clk_5), 117 | .TMDS_Clk_p (hdmi_tx_clk_p), 118 | .TMDS_Clk_n (hdmi_tx_clk_n), 119 | .TMDS_Data_p (hdmi_tx_p), 120 | .TMDS_Data_n (hdmi_tx_n) 121 | ); 122 | 123 | endmodule 124 | -------------------------------------------------------------------------------- /firmware/src/memmove.S: -------------------------------------------------------------------------------- 1 | .extern memcpy 2 | .balign 16, 1 3 | .global memmove 4 | .type memmove, @function 5 | memmove: 6 | /* 7 | * Here we determine if forward copy is possible. Forward copy is 8 | * preferred to backward copy as it is more cache friendly. 9 | * 10 | * If a0 >= a1, t0 gives their distance, if t0 >= a2 then we can 11 | * copy forward. 12 | * If a0 < a1, we can always copy forward. This will make t0 negative, 13 | * so a *unsigned* comparison will always have t0 >= a2. 14 | * 15 | * For forward copy we just delegate the task to memcpy. 16 | */ 17 | sub t0, a0, a1 18 | bltu t0, a2, 1f 19 | tail memcpy 20 | 1: 21 | 22 | /* 23 | * Register allocation for code below: 24 | * a0 - end of uncopied dst 25 | * a1 - end of uncopied src 26 | * t0 - start of uncopied dst 27 | */ 28 | mv t0, a0 29 | add a0, a0, a2 30 | add a1, a1, a2 31 | 32 | /* 33 | * Use bytewise copy if too small. 34 | * 35 | * This threshold must be at least 16 to ensure at least one wordwise copy 36 | * is performed. This will save at least 7 iterations of bytewise copy, 37 | * which pays off the fixed overhead. 38 | */ 39 | li a3, 16 40 | bltu a2, a3, .Lbyte_copy_tail 41 | 42 | /* 43 | * Bytewise copy first to align t0 to word boundary. 44 | */ 45 | andi a2, a0, ~7 46 | beq a0, a2, 2f 47 | 1: 48 | addi a1, a1, -1 49 | lb a5, 0(a1) 50 | addi a0, a0, -1 51 | sb a5, 0(a0) 52 | bne a0, a2, 1b 53 | 2: 54 | 55 | /* 56 | * Now a0 is word-aligned. If a1 is also word aligned, we could perform 57 | * aligned word-wise copy. Otherwise we need to perform misaligned 58 | * word-wise copy. 59 | */ 60 | andi a3, a1, 7 61 | bnez a3, .Lmisaligned_word_copy 62 | 63 | /* Wordwise copy */ 64 | addi t0, t0, 8-1 65 | bleu a0, t0, 2f 66 | 1: 67 | addi a1, a1, -8 68 | ld a5, 0(a1) 69 | addi a0, a0, -8 70 | sd a5, 0(a0) 71 | bgtu a0, t0, 1b 72 | 2: 73 | addi t0, t0, -(8-1) 74 | 75 | .Lbyte_copy_tail: 76 | /* 77 | * Bytewise copy anything left. 78 | */ 79 | beq a0, t0, 2f 80 | 1: 81 | addi a1, a1, -1 82 | lb a5, 0(a1) 83 | addi a0, a0, -1 84 | sb a5, 0(a0) 85 | bne a0, t0, 1b 86 | 2: 87 | 88 | mv a0, t0 89 | ret 90 | 91 | .Lmisaligned_word_copy: 92 | /* 93 | * Misaligned word-wise copy. 94 | * For misaligned copy we still perform word-wise copy, but we need to 95 | * use the value fetched from the previous iteration and do some shifts. 96 | * This is safe because we wouldn't access more words than necessary. 97 | */ 98 | 99 | /* Calculate shifts */ 100 | slli t3, a3, 3 101 | sub t4, x0, t3 /* negate is okay as shift will only look at LSBs */ 102 | 103 | /* Load the initial value and align a1 */ 104 | andi a1, a1, ~7 105 | ld a5, 0(a1) 106 | 107 | addi t0, t0, 8-1 108 | /* At least one iteration will be executed here, no check */ 109 | 1: 110 | sll a4, a5, t4 111 | addi a1, a1, -8 112 | ld a5, 0(a1) 113 | srl a2, a5, t3 114 | or a2, a2, a4 115 | addi a0, a0, -8 116 | sd a2, 0(a0) 117 | bgtu a0, t0, 1b 118 | 119 | /* Update pointers to correct value */ 120 | addi t0, t0, -(8-1) 121 | add a1, a1, a3 122 | 123 | j .Lbyte_copy_tail 124 | .size memmove, . - memmove 125 | -------------------------------------------------------------------------------- /genesys_2/rtl/muntjac_core_wrapper.sv: -------------------------------------------------------------------------------- 1 | `include "tl_util.svh" 2 | 3 | module muntjac_core_wrapper import muntjac_pkg::*; #( 4 | parameter DataWidth = 64, 5 | parameter PhysAddrLen = 56, 6 | parameter rv64f_e RV64F = RV64FNone, 7 | parameter int unsigned DCacheWaysWidth = 2, 8 | parameter int unsigned DCacheSetsWidth = 6, 9 | parameter int unsigned ICacheWaysWidth = 2, 10 | parameter int unsigned ICacheSetsWidth = 6, 11 | parameter int unsigned DTlbNumWays = 4, 12 | parameter int unsigned DTlbSetsWidth = 3, 13 | parameter int unsigned ITlbNumWays = 4, 14 | parameter int unsigned ITlbSetsWidth = 3, 15 | parameter int unsigned MHPMCounterNum = 0, 16 | parameter bit MHPMICacheEnable = 1'b0, 17 | parameter bit MHPMDCacheEnable = 1'b0, 18 | parameter int unsigned SourceWidth = 4, 19 | parameter int unsigned SinkWidth = 1 20 | ) ( 21 | // Clock and reset 22 | input logic clk_i, 23 | input logic rst_ni, 24 | 25 | // Memory interface 26 | `TL_DECLARE_HOST_PORT(DataWidth, PhysAddrLen, SourceWidth, SinkWidth, mem), 27 | 28 | input logic irq_software_m_i, 29 | input logic irq_timer_m_i, 30 | input logic irq_external_m_i, 31 | input logic irq_external_s_i, 32 | 33 | input logic [63:0] hart_id_i, 34 | 35 | input logic [HPM_EVENT_NUM-1:0] hpm_event_i, 36 | 37 | // Debug connections 38 | output instr_trace_t dbg_o 39 | ); 40 | 41 | if (DataWidth == 128 && 42 | PhysAddrLen == 38 && 43 | RV64F == RV64FFull && 44 | DCacheWaysWidth == 2 && 45 | DCacheSetsWidth == 6 && 46 | ICacheWaysWidth == 2 && 47 | ICacheSetsWidth == 6 && 48 | DTlbNumWays == 32 && 49 | DTlbSetsWidth == 0 && 50 | ITlbNumWays == 32 && 51 | ITlbSetsWidth == 0 && 52 | MHPMCounterNum == 0 && 53 | MHPMICacheEnable == 1'b0 && 54 | MHPMDCacheEnable == 1'b0 && 55 | SourceWidth == 2 && 56 | SinkWidth == 4 57 | ) begin: preset1 58 | 59 | muntjac_core_preset1 core ( 60 | .clk_i, 61 | .rst_ni, 62 | `TL_FORWARD_HOST_PORT(mem, mem), 63 | .irq_software_m_i, 64 | .irq_timer_m_i, 65 | .irq_external_m_i, 66 | .irq_external_s_i, 67 | .hart_id_i, 68 | .hpm_event_i, 69 | .dbg_o 70 | ); 71 | 72 | end else begin: no_preset 73 | 74 | $warning("Preset is not used due to parameter mismatch"); 75 | 76 | muntjac_core #( 77 | .DataWidth (DataWidth), 78 | .PhysAddrLen (PhysAddrLen), 79 | .RV64F (RV64F), 80 | .DCacheWaysWidth (DCacheWaysWidth), 81 | .DCacheSetsWidth (DCacheSetsWidth), 82 | .ICacheWaysWidth (ICacheWaysWidth), 83 | .ICacheSetsWidth (ICacheSetsWidth), 84 | .DTlbNumWays (DTlbNumWays), 85 | .DTlbSetsWidth (DTlbSetsWidth), 86 | .ITlbNumWays (ITlbNumWays), 87 | .ITlbSetsWidth (ITlbSetsWidth), 88 | .MHPMCounterNum (MHPMCounterNum), 89 | .MHPMICacheEnable (MHPMICacheEnable), 90 | .MHPMDCacheEnable (MHPMDCacheEnable), 91 | .SourceWidth (SourceWidth), 92 | .SinkWidth (SinkWidth) 93 | ) core ( 94 | .clk_i, 95 | .rst_ni, 96 | `TL_FORWARD_HOST_PORT(mem, mem), 97 | .irq_software_m_i, 98 | .irq_timer_m_i, 99 | .irq_external_m_i, 100 | .irq_external_s_i, 101 | .hart_id_i, 102 | .hpm_event_i, 103 | .dbg_o 104 | ); 105 | 106 | end 107 | 108 | endmodule 109 | -------------------------------------------------------------------------------- /firmware/src/interp.rs: -------------------------------------------------------------------------------- 1 | use riscv::{Csr, Op}; 2 | 3 | use super::fp; 4 | use super::Context; 5 | use super::TrapInfo; 6 | 7 | macro_rules! trap { 8 | ($cause: expr, $tval: expr) => { 9 | return Err(TrapInfo { 10 | cause: $cause, 11 | tval: $tval, 12 | }) 13 | }; 14 | } 15 | 16 | /// Perform a CSR read on a context. 17 | fn read_csr(ctx: &mut Context, csr: Csr) -> Result { 18 | Ok(match csr { 19 | Csr::Time => super::timer::time_u64() as usize, 20 | #[cfg(feature = "fp-mem")] 21 | Csr::Fflags | Csr::Frm | Csr::Fcsr => return fp::read_csr(ctx, csr), 22 | _ => trap!(2, 0), 23 | }) 24 | } 25 | 26 | fn write_csr(ctx: &mut Context, csr: Csr, value: usize) -> Result<(), TrapInfo> { 27 | match csr { 28 | #[cfg(feature = "fp-mem")] 29 | Csr::Fflags | Csr::Frm | Csr::Fcsr => return fp::write_csr(ctx, csr, value), 30 | _ => trap!(2, 0), 31 | } 32 | } 33 | 34 | pub fn step(ctx: &mut Context, op: &Op) -> Result<(), TrapInfo> { 35 | macro_rules! read_reg { 36 | ($rs: expr) => {{ 37 | let rs = $rs as usize; 38 | if rs >= 32 { 39 | unsafe { core::hint::unreachable_unchecked() } 40 | } 41 | ctx.registers[rs] 42 | }}; 43 | } 44 | macro_rules! write_reg { 45 | ($rd: expr, $expression:expr) => {{ 46 | let rd = $rd as usize; 47 | let value: usize = $expression; 48 | if rd >= 32 { 49 | unsafe { core::hint::unreachable_unchecked() } 50 | } 51 | if rd != 0 { 52 | ctx.registers[rd] = value 53 | } 54 | }}; 55 | } 56 | 57 | match *op { 58 | /* CSR */ 59 | Op::Csrrw { rd, rs1, csr } => { 60 | let result = if rd != 0 { read_csr(ctx, csr)? } else { 0 }; 61 | write_csr(ctx, csr, read_reg!(rs1))?; 62 | write_reg!(rd, result); 63 | } 64 | Op::Csrrs { rd, rs1, csr } => { 65 | let result = read_csr(ctx, csr)?; 66 | if rs1 != 0 { 67 | write_csr(ctx, csr, result | read_reg!(rs1))? 68 | } 69 | write_reg!(rd, result); 70 | } 71 | Op::Csrrc { rd, rs1, csr } => { 72 | let result = read_csr(ctx, csr)?; 73 | if rs1 != 0 { 74 | write_csr(ctx, csr, result & !read_reg!(rs1))? 75 | } 76 | write_reg!(rd, result); 77 | } 78 | Op::Csrrwi { rd, imm, csr } => { 79 | let result = if rd != 0 { read_csr(ctx, csr)? } else { 0 }; 80 | write_csr(ctx, csr, imm as usize)?; 81 | write_reg!(rd, result); 82 | } 83 | Op::Csrrsi { rd, imm, csr } => { 84 | let result = read_csr(ctx, csr)?; 85 | if imm != 0 { 86 | write_csr(ctx, csr, result | imm as usize)? 87 | } 88 | write_reg!(rd, result); 89 | } 90 | Op::Csrrci { rd, imm, csr } => { 91 | let result = read_csr(ctx, csr)?; 92 | if imm != 0 { 93 | write_csr(ctx, csr, result & !imm as usize)? 94 | } 95 | write_reg!(rd, result); 96 | } 97 | 98 | _ => { 99 | #[cfg(feature = "fp-mem")] 100 | if fp::is_fp(op) { 101 | return fp::step(ctx, op); 102 | } 103 | trap!(2, 0); 104 | } 105 | } 106 | 107 | Ok(()) 108 | } 109 | -------------------------------------------------------------------------------- /firmware/linker.tpl.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT("elf64-littleriscv") 2 | OUTPUT_ARCH(riscv) 3 | ENTRY(_start) 4 | 5 | SECTIONS 6 | { 7 | . = 0; 8 | __executable_start = .; 9 | 10 | .text.startup : { 11 | *(.text.start) 12 | *(.text.startup .text.startup.*) 13 | } 14 | .text.unlikely : { 15 | *(.text.unlikely .text.unlikely.*) 16 | } 17 | .rodata.startup : { 18 | *(.rodata.startup .rodata.startup.*) 19 | } 20 | .rodata.unlikely : { 21 | *(.rodata.unlikely .rodata.unlikely.*) 22 | } 23 | 24 | .eh_frame_hdr : { *(.eh_frame_hdr) *(.eh_frame_entry .eh_frame_entry.*) } 25 | .eh_frame : { KEEP (*(.eh_frame)) *(.eh_frame.*) } 26 | .gcc_except_table : { *(.gcc_except_table .gcc_except_table.*) } 27 | 28 | /* Thread Local Storage sections */ 29 | .tdata : 30 | { 31 | __tdata_start = .; 32 | *(.tdata .tdata.*) 33 | __tdata_end = .; 34 | } 35 | .tbss : 36 | { 37 | *(.tbss .tbss.*) 38 | __tbss_end = .; 39 | } 40 | 41 | __text_start_phys = .; 42 | . = ${MEMORY_LIMIT} - 0x200000; 43 | __text_start = .; 44 | 45 | /* Set LMA address here, and all following sections will get correct LMA */ 46 | .text : AT(__text_start_phys) 47 | { 48 | *(.text .text.*) 49 | } 50 | 51 | PROVIDE(memcpy_phys = memcpy - __text_start + __text_start_phys); 52 | PROVIDE(memset_phys = memset - __text_start + __text_start_phys); 53 | PROVIDE(__etext = .); 54 | 55 | .rodata : { *(.rodata .rodata.*) } 56 | .sdata2 : { *(.sdata2 .sdata2.*) } 57 | .sbss2 : { *(.sbss2 .sbss2.* ) } 58 | 59 | . = ALIGN(8); 60 | .data : { 61 | __DATA_BEGIN__ = .; 62 | *(.data .data.*) 63 | } 64 | 65 | /* We want the small data sections together, so single-instruction offsets 66 | can access them all, and initialized data all before uninitialized, so 67 | we can shorten the on-disk segment size. */ 68 | . = ALIGN(8); 69 | .sdata : { 70 | __SDATA_BEGIN__ = .; 71 | *(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*) 72 | *(.sdata .sdata.*) 73 | } 74 | 75 | __bss_start = .; 76 | __bss_start_phys = __bss_start - __text_start + __text_start_phys; 77 | .sbss : { *(.sbss .sbss.*) } 78 | .bss : { *(.bss .bss.*) } 79 | . = ALIGN(64 / 8); 80 | __BSS_END__ = .; 81 | __global_pointer$ = MIN(__SDATA_BEGIN__ + 0x800, MAX(__DATA_BEGIN__ + 0x800, __BSS_END__ - 0x800)); 82 | 83 | _end = .; 84 | 85 | /* DWARF debug sections. 86 | Symbols in the DWARF debugging sections are relative to the beginning 87 | of the section so we begin them at 0. */ 88 | .debug_aranges 0 : { *(.debug_aranges) } 89 | .debug_pubnames 0 : { *(.debug_pubnames) } 90 | .debug_info 0 : { *(.debug_info) } 91 | .debug_abbrev 0 : { *(.debug_abbrev) } 92 | .debug_line 0 : { *(.debug_line) } 93 | .debug_frame 0 : { *(.debug_frame) } 94 | .debug_str 0 : { *(.debug_str) } 95 | .debug_loc 0 : { *(.debug_loc) } 96 | .debug_macinfo 0 : { *(.debug_macinfo) } 97 | /* DWARF 3 */ 98 | .debug_pubtypes 0 : { *(.debug_pubtypes) } 99 | .debug_ranges 0 : { *(.debug_ranges) } 100 | /* DWARF 5 */ 101 | .debug_addr 0 : { *(.debug_addr) } 102 | .debug_line_str 0 : { *(.debug_line_str) } 103 | .debug_loclists 0 : { *(.debug_loclists) } 104 | .debug_macro 0 : { *(.debug_macro) } 105 | .debug_names 0 : { *(.debug_names) } 106 | .debug_rnglists 0 : { *(.debug_rnglists) } 107 | .debug_str_offsets 0 : { *(.debug_str_offsets) } 108 | .debug_sup 0 : { *(.debug_sup) } 109 | 110 | /DISCARD/ : { *.* } 111 | } 112 | 113 | -------------------------------------------------------------------------------- /firmware/src/fmt.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_macros)] 2 | 3 | use core::fmt::Write; 4 | 5 | pub struct Console; 6 | 7 | pub static CONSOLE: spin::Mutex = spin::Mutex::new(Console); 8 | 9 | impl Write for Console { 10 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 11 | for byte in s.bytes() { 12 | if byte == b'\n' { 13 | super::uart::uart_send_byte(b'\r'); 14 | } 15 | super::uart::uart_send_byte(byte); 16 | } 17 | Ok(()) 18 | } 19 | } 20 | 21 | pub fn console_write(args: core::fmt::Arguments<'_>) { 22 | let mut guard = CONSOLE.lock(); 23 | guard.write_fmt(args).unwrap(); 24 | #[cfg(all(has_display, feature = "fbcon"))] 25 | { 26 | if let Some(fbcon) = unsafe { crate::video::get_fbcon() } { 27 | fbcon.write_fmt(args).unwrap(); 28 | } 29 | } 30 | } 31 | 32 | macro_rules! format_args_nl { 33 | ($($args:tt)*) => (format_args!("{}\n", format_args!($($args)*))) 34 | } 35 | 36 | macro_rules! println { 37 | ($($args:tt)*) => ({ 38 | crate::fmt::console_write(format_args_nl!($($args)*)) 39 | }) 40 | } 41 | 42 | macro_rules! print { 43 | ($($arg:tt)*) => ({ 44 | crate::fmt::console_write(format_args!($($arg)*)) 45 | }) 46 | } 47 | 48 | macro_rules! dbg { 49 | () => { 50 | println!("[{}:{}]", file!(), line!()); 51 | }; 52 | ($val:expr) => { 53 | // Use of `match` here is intentional because it affects the lifetimes 54 | // of temporaries - https://stackoverflow.com/a/48732525/1063961 55 | match $val { 56 | tmp => { 57 | println!("[{}:{}] {} = {:#?}", 58 | file!(), line!(), stringify!($val), &tmp); 59 | tmp 60 | } 61 | } 62 | }; 63 | // Trailing comma with single argument is ignored 64 | ($val:expr,) => { dbg!($val) }; 65 | ($($val:expr),+ $(,)?) => { 66 | ($(dbg!($val)),+,) 67 | }; 68 | } 69 | 70 | struct Logger; 71 | 72 | impl log::Log for Logger { 73 | fn enabled(&self, metadata: &log::Metadata) -> bool { 74 | metadata.level() <= log::Level::Debug 75 | } 76 | 77 | fn log(&self, record: &log::Record) { 78 | if self.enabled(record.metadata()) { 79 | let color = match record.level() { 80 | log::Level::Trace => "35", 81 | log::Level::Debug => "34", 82 | log::Level::Info => "32", 83 | log::Level::Warn => "33", 84 | log::Level::Error => "31", 85 | }; 86 | 87 | let mut guard = CONSOLE.lock(); 88 | guard.write_fmt(format_args!("\x1b[{color}m")).unwrap(); 89 | 90 | match format_args!( 91 | "{level}:{target}:{msg}\n", 92 | level = record.level(), 93 | target = record.target(), 94 | msg = record.args() 95 | ) { 96 | fmt => { 97 | guard.write_fmt(fmt).unwrap(); 98 | #[cfg(all(has_display, feature = "fbcon"))] 99 | { 100 | if let Some(fbcon) = unsafe { crate::video::get_fbcon() } { 101 | fbcon.write_fmt(fmt).unwrap(); 102 | } 103 | } 104 | } 105 | } 106 | 107 | guard.write_str("\x1b[0m").unwrap(); 108 | } 109 | } 110 | fn flush(&self) {} 111 | } 112 | 113 | pub fn logger_init() { 114 | log::set_logger(&Logger).unwrap(); 115 | log::set_max_level(log::LevelFilter::Trace); 116 | } 117 | -------------------------------------------------------------------------------- /nexys_video/data/pins.xdc: -------------------------------------------------------------------------------- 1 | set_property PACKAGE_PIN R4 [get_ports sys_clk_i] 2 | set_property IOSTANDARD LVCMOS33 [get_ports sys_clk_i] 3 | 4 | set_property PACKAGE_PIN G4 [get_ports sys_rst_ni] 5 | set_property IOSTANDARD LVCMOS15 [get_ports sys_rst_ni] 6 | 7 | set_property PACKAGE_PIN T19 [get_ports { qspi_csn }] 8 | set_property IOSTANDARD LVCMOS33 [get_ports { qspi_csn }] 9 | set_property PACKAGE_PIN P22 [get_ports {qspi_dq[0]}] 10 | set_property IOSTANDARD LVCMOS33 [get_ports {qspi_dq[0]}] 11 | set_property PACKAGE_PIN R22 [get_ports {qspi_dq[1]}] 12 | set_property IOSTANDARD LVCMOS33 [get_ports {qspi_dq[1]}] 13 | #set_property PACKAGE_PIN P21 [get_ports { qspi_dq[2] }] 14 | #set_property IOSTANDARD LVCMOS33 [get_ports { qspi_dq[2] }] 15 | #set_property PACKAGE_PIN R21 [get_ports { qspi_dq[3] }] 16 | #set_property IOSTANDARD LVCMOS33 [get_ports { qspi_dq[3] }] 17 | 18 | set_property PACKAGE_PIN AA19 [get_ports uart_tx] 19 | set_property IOSTANDARD LVCMOS33 [get_ports uart_tx] 20 | set_property PACKAGE_PIN V18 [get_ports uart_rx] 21 | set_property IOSTANDARD LVCMOS33 [get_ports uart_rx] 22 | 23 | set_property PACKAGE_PIN V20 [get_ports sd_reset] 24 | set_property IOSTANDARD LVCMOS33 [get_ports sd_reset] 25 | set_property PACKAGE_PIN T18 [get_ports sd_cd] 26 | set_property IOSTANDARD LVCMOS33 [get_ports sd_cd] 27 | set_property PACKAGE_PIN W19 [get_ports sd_sck] 28 | set_property IOSTANDARD LVCMOS33 [get_ports sd_sck] 29 | set_property PACKAGE_PIN W20 [get_ports sd_cmd] 30 | set_property IOSTANDARD LVCMOS33 [get_ports sd_cmd] 31 | set_property PACKAGE_PIN V19 [get_ports {sd_dat[0]}] 32 | set_property IOSTANDARD LVCMOS33 [get_ports {sd_dat[0]}] 33 | set_property PACKAGE_PIN T21 [get_ports {sd_dat[1]}] 34 | set_property IOSTANDARD LVCMOS33 [get_ports {sd_dat[1]}] 35 | set_property PACKAGE_PIN T20 [get_ports {sd_dat[2]}] 36 | set_property IOSTANDARD LVCMOS33 [get_ports {sd_dat[2]}] 37 | set_property PACKAGE_PIN U18 [get_ports {sd_dat[3]}] 38 | set_property IOSTANDARD LVCMOS33 [get_ports {sd_dat[3]}] 39 | 40 | #set_property PACKAGE_PIN AA4 [get_ports { hdmi_tx_cec }] 41 | #set_property IOSTANDARD LVCMOS33 [get_ports { hdmi_tx_cec }] 42 | set_property PACKAGE_PIN U1 [get_ports { hdmi_tx_clk_n }] 43 | set_property IOSTANDARD TMDS_33 [get_ports { hdmi_tx_clk_n }] 44 | set_property PACKAGE_PIN T1 [get_ports { hdmi_tx_clk_p }] 45 | set_property IOSTANDARD TMDS_33 [get_ports { hdmi_tx_clk_p }] 46 | #set_property PACKAGE_PIN AB13 [get_ports { hdmi_tx_hpd }] 47 | #set_property IOSTANDARD LVCMOS25 [get_ports { hdmi_tx_hpd }] 48 | #set_property PACKAGE_PIN U3 [get_ports { hdmi_tx_scl }] 49 | #set_property IOSTANDARD LVCMOS33 [get_ports { hdmi_tx_scl }] 50 | #set_property PACKAGE_PIN V3 [get_ports { hdmi_tx_sda }] 51 | #set_property IOSTANDARD LVCMOS33 [get_ports { hdmi_tx_sda }] 52 | set_property PACKAGE_PIN Y1 [get_ports { hdmi_tx_n[0] }] 53 | set_property IOSTANDARD TMDS_33 [get_ports { hdmi_tx_n[0] }] 54 | set_property PACKAGE_PIN W1 [get_ports { hdmi_tx_p[0] }] 55 | set_property IOSTANDARD TMDS_33 [get_ports { hdmi_tx_p[0] }] 56 | set_property PACKAGE_PIN AB1 [get_ports { hdmi_tx_n[1] }] 57 | set_property IOSTANDARD TMDS_33 [get_ports { hdmi_tx_n[1] }] 58 | set_property PACKAGE_PIN AA1 [get_ports { hdmi_tx_p[1] }] 59 | set_property IOSTANDARD TMDS_33 [get_ports { hdmi_tx_p[1] }] 60 | set_property PACKAGE_PIN AB2 [get_ports { hdmi_tx_n[2] }] 61 | set_property IOSTANDARD TMDS_33 [get_ports { hdmi_tx_n[2] }] 62 | set_property PACKAGE_PIN AB3 [get_ports { hdmi_tx_p[2] }] 63 | set_property IOSTANDARD TMDS_33 [get_ports { hdmi_tx_p[2] }] 64 | 65 | set_property PACKAGE_PIN W17 [get_ports { ps2_clk }] 66 | set_property IOSTANDARD LVCMOS33 [get_ports { ps2_clk }] 67 | set_property PULLUP true [get_ports { ps2_clk }] 68 | set_property PACKAGE_PIN N13 [get_ports { ps2_dat }] 69 | set_property IOSTANDARD LVCMOS33 [get_ports { ps2_dat }] 70 | set_property PULLUP true [get_ports { ps2_dat }] 71 | -------------------------------------------------------------------------------- /nexys_a7/data/pins.xdc: -------------------------------------------------------------------------------- 1 | set_property PACKAGE_PIN E3 [get_ports sys_clk_i] 2 | set_property IOSTANDARD LVCMOS33 [get_ports sys_clk_i] 3 | 4 | set_property PACKAGE_PIN C12 [get_ports sys_rst_ni] 5 | set_property IOSTANDARD LVCMOS33 [get_ports sys_rst_ni] 6 | 7 | set_property PACKAGE_PIN K17 [get_ports {qspi_dq[0]}] 8 | set_property IOSTANDARD LVCMOS33 [get_ports {qspi_dq[0]}] 9 | set_property PACKAGE_PIN K18 [get_ports {qspi_dq[1]}] 10 | set_property IOSTANDARD LVCMOS33 [get_ports {qspi_dq[1]}] 11 | #set_property PACKAGE_PIN L14 [get_ports {qspi_dq[2]}] 12 | #set_property IOSTANDARD LVCMOS33 [get_ports {qspi_dq[2]}] 13 | #set_property PACKAGE_PIN M14 [get_ports {qspi_dq[3]}] 14 | #set_property IOSTANDARD LVCMOS33 [get_ports {qspi_dq[3]}] 15 | set_property PACKAGE_PIN L13 [get_ports qspi_csn] 16 | set_property IOSTANDARD LVCMOS33 [get_ports qspi_csn] 17 | 18 | set_property PACKAGE_PIN D4 [get_ports uart_tx] 19 | set_property IOSTANDARD LVCMOS33 [get_ports uart_tx] 20 | set_property PACKAGE_PIN C4 [get_ports uart_rx] 21 | set_property IOSTANDARD LVCMOS33 [get_ports uart_rx] 22 | set_property PACKAGE_PIN D3 [get_ports uart_cts] 23 | set_property IOSTANDARD LVCMOS33 [get_ports uart_cts] 24 | set_property PACKAGE_PIN E5 [get_ports uart_rts] 25 | set_property IOSTANDARD LVCMOS33 [get_ports uart_rts] 26 | 27 | set_property PACKAGE_PIN E2 [get_ports sd_reset] 28 | set_property IOSTANDARD LVCMOS33 [get_ports sd_reset] 29 | set_property PACKAGE_PIN A1 [get_ports sd_cd] 30 | set_property IOSTANDARD LVCMOS33 [get_ports sd_cd] 31 | set_property PACKAGE_PIN B1 [get_ports sd_sck] 32 | set_property IOSTANDARD LVCMOS33 [get_ports sd_sck] 33 | set_property PACKAGE_PIN C1 [get_ports sd_cmd] 34 | set_property IOSTANDARD LVCMOS33 [get_ports sd_cmd] 35 | set_property PACKAGE_PIN C2 [get_ports {sd_dat[0]}] 36 | set_property IOSTANDARD LVCMOS33 [get_ports {sd_dat[0]}] 37 | set_property PACKAGE_PIN E1 [get_ports {sd_dat[1]}] 38 | set_property IOSTANDARD LVCMOS33 [get_ports {sd_dat[1]}] 39 | set_property PACKAGE_PIN F1 [get_ports {sd_dat[2]}] 40 | set_property IOSTANDARD LVCMOS33 [get_ports {sd_dat[2]}] 41 | set_property PACKAGE_PIN D2 [get_ports {sd_dat[3]}] 42 | set_property IOSTANDARD LVCMOS33 [get_ports {sd_dat[3]}] 43 | 44 | set_property PACKAGE_PIN C9 [get_ports mdc] 45 | set_property IOSTANDARD LVCMOS33 [get_ports mdc] 46 | set_property PACKAGE_PIN A9 [get_ports mdio] 47 | set_property IOSTANDARD LVCMOS33 [get_ports mdio] 48 | set_property PACKAGE_PIN D5 [get_ports rmii_ref_clk] 49 | set_property IOSTANDARD LVCMOS33 [get_ports rmii_ref_clk] 50 | set_property PACKAGE_PIN A10 [get_ports rmii_txd[0]] 51 | set_property IOSTANDARD LVCMOS33 [get_ports rmii_txd[0]] 52 | #set_property IOB true [get_ports rmii_txd[0]] 53 | set_property PACKAGE_PIN A8 [get_ports rmii_txd[1]] 54 | set_property IOSTANDARD LVCMOS33 [get_ports rmii_txd[1]] 55 | #set_property IOB true [get_ports rmii_txd[1]] 56 | set_property PACKAGE_PIN B9 [get_ports rmii_tx_en] 57 | set_property IOSTANDARD LVCMOS33 [get_ports rmii_tx_en] 58 | #set_property IOB true [get_ports rmii_tx_en] 59 | set_property PACKAGE_PIN C11 [get_ports rmii_rxd[0]] 60 | set_property IOSTANDARD LVCMOS33 [get_ports rmii_rxd[0]] 61 | #set_property IOB true [get_ports rmii_rxd[0]] 62 | set_property PACKAGE_PIN D10 [get_ports rmii_rxd[1]] 63 | set_property IOSTANDARD LVCMOS33 [get_ports rmii_rxd[1]] 64 | #set_property IOB true [get_ports rmii_rxd[1]] 65 | set_property PACKAGE_PIN D9 [get_ports rmii_csr_dv] 66 | set_property IOSTANDARD LVCMOS33 [get_ports rmii_csr_dv] 67 | #set_property IOB true [get_ports rmii_csr_dv] 68 | set_property PACKAGE_PIN C10 [get_ports rmii_rx_er] 69 | set_property IOSTANDARD LVCMOS33 [get_ports rmii_rx_er] 70 | #set_property IOB true [get_ports rmii_rx_er] 71 | set_property PACKAGE_PIN B3 [get_ports phy_rst_n] 72 | set_property IOSTANDARD LVCMOS33 [get_ports phy_rst_n] 73 | set_property PACKAGE_PIN B8 [get_ports phy_irq_i] 74 | set_property IOSTANDARD LVCMOS33 [get_ports phy_irq_i] 75 | -------------------------------------------------------------------------------- /firmware/src/flash.rs: -------------------------------------------------------------------------------- 1 | use core::time::Duration; 2 | 3 | const SPI_BASE: *mut u32 = 0x1005_0000 as _; 4 | 5 | // Global interrupt enable register [Write] 6 | const SPI_GIER: *mut u32 = (0x1005_0000 + 0x7 * 4) as _; 7 | 8 | // // IP interrupt status register [Read/Toggle to write] 9 | // #define SPI_ISR 0x08u 10 | 11 | // // IP interrupt enable register [Read/Write] 12 | // #define SPI_IER 0x0Au 13 | 14 | // Software reset register [Write] 15 | const SPI_SRR: *mut u32 = (0x1005_0000 + 0x10 * 4) as _; 16 | 17 | // SPI control register [Read/Write] 18 | const SPI_CR: *mut u32 = (0x1005_0000 + 0x18 * 4) as _; 19 | 20 | // SPI status register [Read] 21 | const SPI_SR: *mut u32 = (0x1005_0000 + 0x19 * 4) as _; 22 | 23 | // SPI data transmit register, FIFO-16 [Write] 24 | const SPI_DTR: *mut u32 = (0x1005_0000 + 0x1A * 4) as _; 25 | 26 | // SPI data receive register, FIFO-16 [Read] 27 | const SPI_DRR: *mut u32 = (0x1005_0000 + 0x1B * 4) as _; 28 | 29 | // SPI Slave select register, [Read/Write] 30 | const SPI_SSR: *mut u32 = (0x1005_0000 + 0x1C * 4) as _; 31 | 32 | // // Transmit FIFO occupancy register [Read] 33 | // #define SPI_TFOR 0x1Du 34 | 35 | // // Receive FIFO occupancy register [Read] 36 | // #define SPI_RFROR 0x1Eu 37 | 38 | pub fn spi_init() { 39 | unsafe { 40 | // disable interrupt, full polling mode 41 | core::ptr::write_volatile(SPI_GIER, 0x0); 42 | 43 | // MSB first, master, reset FIFOs, SPI enabled, clock 00 mode 44 | core::ptr::write_volatile(SPI_CR, 0x86 | 0b11000); 45 | } 46 | } 47 | 48 | pub fn test() { 49 | spi_init(); 50 | 51 | // Make STARTUP work 52 | spi_send(&[0; 16]); 53 | 54 | let mut recv_buf = [0u8; 10]; 55 | spi_select_slave(); 56 | spi_send(&[0xB, 0, 0, 0, 0]); 57 | spi_recv(&mut recv_buf); 58 | spi_deselect_slave(); 59 | spi_fini(); 60 | for &b in recv_buf.iter() { 61 | println!("{:x}", b); 62 | } 63 | } 64 | 65 | pub fn spi_fini() { 66 | unsafe { 67 | core::ptr::write_volatile(SPI_CR, 0xE4); 68 | } 69 | } 70 | 71 | pub fn spi_send(tx: &[u8]) { 72 | for tx in tx.chunks(16) { 73 | for byte in tx { 74 | unsafe { 75 | core::ptr::write_volatile(SPI_DTR, *byte as u32); 76 | } 77 | } 78 | while unsafe { core::ptr::read_volatile(SPI_SR) } & 4 == 0 {} 79 | unsafe { core::ptr::write_volatile(SPI_CR, 0xC6) } 80 | } 81 | } 82 | 83 | pub fn spi_recv(rx: &mut [u8]) { 84 | for rx in rx.chunks_mut(16) { 85 | for _ in 0..rx.len() { 86 | unsafe { 87 | core::ptr::write_volatile(SPI_DTR, 0xFF); 88 | } 89 | } 90 | while unsafe { core::ptr::read_volatile(SPI_SR) } & 4 == 0 {} 91 | for byte in rx { 92 | *byte = unsafe { core::ptr::read_volatile(SPI_DRR) } as u8; 93 | } 94 | } 95 | } 96 | 97 | pub fn spi_exchange(tx: &[u8], rx: &mut [u8]) { 98 | assert_eq!(tx.len(), rx.len()); 99 | for (tx, rx) in tx.chunks(16).zip(rx.chunks_mut(16)) { 100 | for byte in tx { 101 | unsafe { 102 | core::ptr::write_volatile(SPI_DTR, *byte as u32); 103 | } 104 | } 105 | while unsafe { core::ptr::read_volatile(SPI_SR) } & 4 == 0 {} 106 | for byte in rx { 107 | *byte = unsafe { core::ptr::read_volatile(SPI_DRR) } as u8; 108 | } 109 | } 110 | } 111 | 112 | pub fn spi_send_byte(tx: u8) { 113 | spi_send_recv_byte(tx); 114 | } 115 | 116 | pub fn spi_send_recv_byte(tx: u8) -> u8 { 117 | let mut ret: u8 = 0; 118 | spi_exchange(&[tx], core::slice::from_mut(&mut ret)); 119 | ret 120 | } 121 | 122 | pub fn spi_recv_byte() -> u8 { 123 | spi_send_recv_byte(0xFF) 124 | } 125 | 126 | pub fn spi_select_slave() { 127 | unsafe { core::ptr::write_volatile(SPI_SSR, 0xFFFFFFFE) } 128 | } 129 | 130 | pub fn spi_deselect_slave() { 131 | unsafe { core::ptr::write_volatile(SPI_SSR, 0xFFFFFFFF) } 132 | } 133 | -------------------------------------------------------------------------------- /firmware/src/memcpy.S: -------------------------------------------------------------------------------- 1 | .balign 16, 1 2 | .global memcpy 3 | .type memcpy, @function 4 | memcpy: 5 | /* Save for return value */ 6 | mv t6, a0 7 | 8 | /* 9 | * Register allocation for code below: 10 | * a0 - start of uncopied dst 11 | * a1 - start of uncopied src 12 | * t0 - end of uncopied dst 13 | */ 14 | add t0, a0, a2 15 | 16 | /* 17 | * Use bytewise copy if too small. 18 | * 19 | * This threshold must be at least 16 to ensure at least one wordwise copy 20 | * is performed. This will save at least 7 iterations of bytewise copy, 21 | * which pays off the fixed overhead. 22 | */ 23 | li a3, 16 24 | bltu a2, a3, .Lbyte_copy_tail 25 | 26 | /* 27 | * Bytewise copy first to align a0 to word boundary. 28 | */ 29 | addi a2, a0, 7 30 | andi a2, a2, ~7 31 | beq a0, a2, 2f 32 | 1: 33 | lb a5, 0(a1) 34 | addi a1, a1, 1 35 | sb a5, 0(a0) 36 | addi a0, a0, 1 37 | bne a0, a2, 1b 38 | 2: 39 | 40 | /* 41 | * Now a0 is word-aligned. If a1 is also word aligned, we could perform 42 | * aligned word-wise copy. Otherwise we need to perform misaligned 43 | * word-wise copy. 44 | */ 45 | andi a3, a1, 7 46 | bnez a3, .Lmisaligned_word_copy 47 | 48 | /* Unrolled wordwise copy */ 49 | addi t0, t0, -(16*8-1) 50 | bgeu a0, t0, 2f 51 | 1: 52 | ld a2, 0*8(a1) 53 | ld a3, 1*8(a1) 54 | ld a4, 2*8(a1) 55 | ld a5, 3*8(a1) 56 | ld a6, 4*8(a1) 57 | ld a7, 5*8(a1) 58 | ld t1, 6*8(a1) 59 | ld t2, 7*8(a1) 60 | ld t3, 8*8(a1) 61 | ld t4, 9*8(a1) 62 | ld t5, 10*8(a1) 63 | sd a2, 0*8(a0) 64 | sd a3, 1*8(a0) 65 | sd a4, 2*8(a0) 66 | sd a5, 3*8(a0) 67 | sd a6, 4*8(a0) 68 | sd a7, 5*8(a0) 69 | sd t1, 6*8(a0) 70 | sd t2, 7*8(a0) 71 | sd t3, 8*8(a0) 72 | sd t4, 9*8(a0) 73 | sd t5, 10*8(a0) 74 | ld a2, 11*8(a1) 75 | ld a3, 12*8(a1) 76 | ld a4, 13*8(a1) 77 | ld a5, 14*8(a1) 78 | ld a6, 15*8(a1) 79 | addi a1, a1, 16*8 80 | sd a2, 11*8(a0) 81 | sd a3, 12*8(a0) 82 | sd a4, 13*8(a0) 83 | sd a5, 14*8(a0) 84 | sd a6, 15*8(a0) 85 | addi a0, a0, 16*8 86 | bltu a0, t0, 1b 87 | 2: 88 | /* Post-loop increment by 16*8-1 and pre-loop decrement by 8-1 */ 89 | addi t0, t0, 15*8 90 | 91 | /* Wordwise copy */ 92 | bgeu a0, t0, 2f 93 | 1: 94 | ld a5, 0(a1) 95 | addi a1, a1, 8 96 | sd a5, 0(a0) 97 | addi a0, a0, 8 98 | bltu a0, t0, 1b 99 | 2: 100 | addi t0, t0, 8-1 101 | 102 | .Lbyte_copy_tail: 103 | /* 104 | * Bytewise copy anything left. 105 | */ 106 | beq a0, t0, 2f 107 | 1: 108 | lb a5, 0(a1) 109 | addi a1, a1, 1 110 | sb a5, 0(a0) 111 | addi a0, a0, 1 112 | bne a0, t0, 1b 113 | 2: 114 | 115 | mv a0, t6 116 | ret 117 | 118 | .Lmisaligned_word_copy: 119 | /* 120 | * Misaligned word-wise copy. 121 | * For misaligned copy we still perform word-wise copy, but we need to 122 | * use the value fetched from the previous iteration and do some shifts. 123 | * This is safe because we wouldn't access more words than necessary. 124 | */ 125 | 126 | /* Calculate shifts */ 127 | slli t3, a3, 3 128 | sub t4, x0, t3 /* negate is okay as shift will only look at LSBs */ 129 | 130 | /* Load the initial value and align a1 */ 131 | andi a1, a1, ~(8-1) 132 | ld a5, 0(a1) 133 | 134 | addi t0, t0, -(8-1) 135 | /* At least one iteration will be executed here, no check */ 136 | 1: 137 | srl a4, a5, t3 138 | ld a5, 8(a1) 139 | addi a1, a1, 8 140 | sll a2, a5, t4 141 | or a2, a2, a4 142 | sd a2, 0(a0) 143 | addi a0, a0, 8 144 | bltu a0, t0, 1b 145 | 146 | /* Update pointers to correct value */ 147 | addi t0, t0, 8-1 148 | add a1, a1, a3 149 | 150 | j .Lbyte_copy_tail 151 | .size memcpy, . - memcpy 152 | -------------------------------------------------------------------------------- /rtl/uncore/clint.sv: -------------------------------------------------------------------------------- 1 | module clint #( 2 | parameter NUM_HARTS = 1, 3 | parameter CLK_FREQ = 40 4 | ) ( 5 | input logic clk, 6 | input logic rstn, 7 | 8 | // We expose the control as a 64KiB BRAM. 9 | input logic [15:0] bram_addr, 10 | input logic bram_en, 11 | input logic [7:0] bram_we, 12 | output logic [63:0] bram_rddata, 13 | input logic [63:0] bram_wrdata, 14 | 15 | input logic timer_clk, 16 | 17 | // IRQ output for each hart. 18 | output logic [NUM_HARTS-1:0] msip, 19 | output logic [NUM_HARTS-1:0] mtip 20 | ); 21 | 22 | if (NUM_HARTS > 4094) begin 23 | $error("CLINT can support at most 4094 harts"); 24 | end 25 | 26 | 27 | // 28 | // Time keeper 29 | // 30 | 31 | logic [$clog2(CLK_FREQ)-1:0] divider; 32 | logic pulse; 33 | 34 | // Divide timer_clk to generate a 1MHz pulse 35 | always_ff @(posedge timer_clk or negedge rstn) begin 36 | if (!rstn) begin 37 | pulse <= 1'b0; 38 | divider <= 0; 39 | end else begin 40 | pulse <= 1'b0; 41 | divider <= divider + 1; 42 | if (divider == CLK_FREQ - 1) begin 43 | pulse <= 1'b1; 44 | divider <= 0; 45 | end 46 | end 47 | end 48 | 49 | logic pulse_o; 50 | prim_pulse_sync sync ( 51 | .clk_src_i (timer_clk), 52 | .rst_src_ni (rstn), 53 | .src_pulse_i (pulse), 54 | .clk_dst_i (clk), 55 | .rst_dst_ni (rstn), 56 | .dst_pulse_o (pulse_o) 57 | ); 58 | 59 | logic [NUM_HARTS-1:0][63:0] mtimecmp; 60 | logic [63:0] mtime; 61 | 62 | always_ff @(posedge clk or negedge rstn) 63 | if (!rstn) begin 64 | mtime <= '0; 65 | mtip <= '0; 66 | end 67 | else begin 68 | if (pulse_o) begin 69 | mtime <= mtime + 1; 70 | end 71 | 72 | // Check if any timer has fired 73 | for (int i = 0; i < NUM_HARTS; i++) begin 74 | mtip[i] <= (mtimecmp[i] <= mtime); 75 | end 76 | end 77 | 78 | // 79 | // BRAM Interfacing logic 80 | // 81 | 82 | always_ff @(posedge clk or negedge rstn) 83 | if (!rstn) begin 84 | bram_rddata <= '0; 85 | for (int i = 0; i < NUM_HARTS; i++) mtimecmp[i] <= '1; 86 | msip <= '0; 87 | end 88 | else begin 89 | if (bram_en) begin 90 | bram_rddata <= 0; 91 | unique case (bram_addr[15:14]) 92 | 2'b00: begin 93 | automatic logic [11:0] hart = {bram_addr[13:3], 1'b0}; 94 | if (hart < NUM_HARTS) begin 95 | bram_rddata <= msip[hart]; 96 | if (bram_we[0]) begin 97 | msip[hart] <= bram_wrdata[0]; 98 | end 99 | if (bram_we[4]) begin 100 | msip[hart + 1] <= bram_wrdata[32]; 101 | end 102 | end 103 | end 104 | 2'b01, 2'b10: begin 105 | automatic logic [11:0] hart = {bram_addr[15], bram_addr[13:3]}; 106 | if (hart == 4095) begin 107 | bram_rddata <= mtime; 108 | end 109 | else if (hart < NUM_HARTS) begin 110 | bram_rddata <= mtimecmp[hart]; 111 | if (|bram_we) begin 112 | mtimecmp[hart] <= bram_wrdata; 113 | end 114 | end 115 | end 116 | default: begin 117 | $warning("CLINT out-of-bound access at %h", bram_addr); 118 | end 119 | endcase 120 | end 121 | end 122 | 123 | endmodule 124 | -------------------------------------------------------------------------------- /firmware/src/video/fbcon.rs: -------------------------------------------------------------------------------- 1 | use byteorder::{ByteOrder, LE}; 2 | use core::fmt::Write; 3 | 4 | pub struct Framebuffer<'a> { 5 | pub framebuffer: &'a mut [u8], 6 | pub width: u32, 7 | pub height: u32, 8 | pub bpl: u32, 9 | pub bpp: u32, 10 | } 11 | 12 | pub struct Fbcon<'a> { 13 | framebuffer: Framebuffer<'a>, 14 | x: u32, 15 | y: u32, 16 | font: psf2::Font<&'static [u8]>, 17 | } 18 | 19 | impl<'a> Write for Fbcon<'a> { 20 | fn write_str(&mut self, s: &str) -> core::fmt::Result { 21 | for c in s.chars() { 22 | if c == '\n' { 23 | self.write_raw_char('\r'); 24 | } 25 | self.write_raw_char(c); 26 | } 27 | Ok(()) 28 | } 29 | } 30 | 31 | impl<'a> Fbcon<'a> { 32 | pub fn new(framebuffer: Framebuffer<'a>) -> Self { 33 | Self { 34 | framebuffer, 35 | x: 0, 36 | y: 0, 37 | font: psf2::Font::new(&include_bytes!("../../../data/bizcat.psf")[..]).unwrap(), 38 | } 39 | } 40 | 41 | pub fn write_raw_char(&mut self, c: char) { 42 | // Handle special characters 43 | match c { 44 | '\x1C' => { 45 | // Clear screen 46 | self.x = 0; 47 | self.y = 0; 48 | self.framebuffer.framebuffer.iter_mut().for_each(|x| *x = 0); 49 | return; 50 | } 51 | '\r' => { 52 | // Carriage return 53 | self.x = 0; 54 | return; 55 | } 56 | '\n' => { 57 | // Newline 58 | self.y += self.font.height(); 59 | return; 60 | } 61 | _ => (), 62 | } 63 | 64 | if self.x + self.font.width() > self.framebuffer.width { 65 | self.x = 0; 66 | self.y += self.font.height(); 67 | } 68 | 69 | if self.y + self.font.height() > self.framebuffer.height { 70 | let scroll_amount = self.y + self.font.height() - self.framebuffer.height; 71 | self.framebuffer 72 | .framebuffer 73 | .copy_within((scroll_amount * self.framebuffer.bpl) as usize.., 0); 74 | } 75 | 76 | let glyph = c.try_into().ok().and_then(|x| self.font.get_ascii(x)); 77 | let glyph = glyph.unwrap_or_else(|| self.font.get_ascii(b'?').unwrap()); 78 | let mut data = glyph.data(); 79 | let mut line_start = self.framebuffer.bpl * self.y + self.x * self.framebuffer.bpp; 80 | for _ in 0..self.font.height() { 81 | let mut fb_ptr = line_start; 82 | for a in 0..self.font.width() as usize { 83 | if data[a / 8] & (1 << (7 - a % 8)) != 0 { 84 | match self.framebuffer.bpp { 85 | 2 => LE::write_u16( 86 | &mut self.framebuffer.framebuffer[fb_ptr as usize..], 87 | 0xFFFF, 88 | ), 89 | 4 => LE::write_u32( 90 | &mut self.framebuffer.framebuffer[fb_ptr as usize..], 91 | 0xFFFFFFFF, 92 | ), 93 | _ => unreachable!(), 94 | } 95 | } else { 96 | match self.framebuffer.bpp { 97 | 2 => LE::write_u16( 98 | &mut self.framebuffer.framebuffer[fb_ptr as usize..], 99 | 0x0000, 100 | ), 101 | 4 => LE::write_u32( 102 | &mut self.framebuffer.framebuffer[fb_ptr as usize..], 103 | 0x00000000, 104 | ), 105 | _ => unreachable!(), 106 | } 107 | } 108 | fb_ptr += self.framebuffer.bpp; 109 | } 110 | data = &data[(self.font.width() as usize + 7) / 8..]; 111 | line_start += self.framebuffer.bpl; 112 | } 113 | 114 | self.x += self.font.width(); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /rtl/uart.sv: -------------------------------------------------------------------------------- 1 | `include "axi_lite_util.svh" 2 | `include "tl_util.svh" 3 | 4 | module uart #( 5 | parameter DataWidth = 32, 6 | parameter AddrWidth = 13, 7 | parameter SourceWidth = 1 8 | ) ( 9 | // Clock and reset 10 | input clk_i, 11 | input rst_ni, 12 | input io_clk_i, 13 | 14 | // IO ports 15 | output uart_tx, 16 | input uart_rx, 17 | input uart_cts, 18 | output uart_rts, 19 | 20 | // TileLink port 21 | `TL_DECLARE_DEVICE_PORT(DataWidth, AddrWidth, SourceWidth, 1, link), 22 | output irq_o 23 | ); 24 | 25 | `AXI_LITE_DECLARE(DataWidth, AddrWidth, axi); 26 | 27 | // Clock wizard 28 | 29 | wire uart_clk; 30 | clk_wiz_uart clk_wiz ( 31 | .clk_in1 (io_clk_i), 32 | .resetn (rst_ni), 33 | .clk_out1 (uart_clk), 34 | .locked () 35 | ); 36 | 37 | wire uart_ctsn = ~uart_cts; 38 | wire uart_rtsn; 39 | assign uart_rts = ~uart_rtsn; 40 | 41 | axi_uart16550_0 uart ( 42 | .s_axi_aclk (io_clk_i), 43 | .s_axi_aresetn (rst_ni), 44 | .ip2intc_irpt (irq_o), 45 | .freeze (1'b0), 46 | .s_axi_awready (axi_aw_ready), 47 | .s_axi_awvalid (axi_aw_valid), 48 | .s_axi_awaddr (axi_aw.addr), 49 | .s_axi_wready (axi_w_ready), 50 | .s_axi_wvalid (axi_w_valid), 51 | .s_axi_wdata (axi_w.data), 52 | .s_axi_wstrb (axi_w.strb), 53 | .s_axi_bready (axi_b_ready), 54 | .s_axi_bvalid (axi_b_valid), 55 | .s_axi_bresp (axi_b.resp), 56 | .s_axi_arready (axi_ar_ready), 57 | .s_axi_arvalid (axi_ar_valid), 58 | .s_axi_araddr (axi_ar.addr), 59 | .s_axi_rready (axi_r_ready), 60 | .s_axi_rvalid (axi_r_valid), 61 | .s_axi_rdata (axi_r.data), 62 | .s_axi_rresp (axi_r.resp), 63 | .baudoutn (), 64 | .ctsn (uart_ctsn), 65 | .dcdn (1'b1), 66 | .ddis (), 67 | .dsrn (1'b1), 68 | .dtrn (), 69 | .out1n (), 70 | .out2n (), 71 | .rin (1'b1), 72 | .rtsn (uart_rtsn), 73 | .rxrdyn (), 74 | .sin (uart_rx), 75 | .sout (uart_tx), 76 | .txrdyn (), 77 | .xin (uart_clk), 78 | .xout () 79 | ); 80 | 81 | // Clock converter 82 | 83 | `AXI_LITE_DECLARE(DataWidth, AddrWidth, axi_sync); 84 | 85 | axi_clock_converter_uart clock_cvt ( 86 | .s_axi_aclk (clk_i), 87 | .s_axi_aresetn (rst_ni), 88 | .s_axi_arready (axi_sync_ar_ready), 89 | .s_axi_arvalid (axi_sync_ar_valid), 90 | .s_axi_araddr (axi_sync_ar.addr), 91 | .s_axi_arprot (axi_sync_ar.prot), 92 | .s_axi_awready (axi_sync_aw_ready), 93 | .s_axi_awvalid (axi_sync_aw_valid), 94 | .s_axi_awaddr (axi_sync_aw.addr), 95 | .s_axi_awprot (axi_sync_aw.prot), 96 | .s_axi_bready (axi_sync_b_ready), 97 | .s_axi_bvalid (axi_sync_b_valid), 98 | .s_axi_bresp (axi_sync_b.resp), 99 | .s_axi_rready (axi_sync_r_ready), 100 | .s_axi_rvalid (axi_sync_r_valid), 101 | .s_axi_rdata (axi_sync_r.data), 102 | .s_axi_rresp (axi_sync_r.resp), 103 | .s_axi_wready (axi_sync_w_ready), 104 | .s_axi_wvalid (axi_sync_w_valid), 105 | .s_axi_wdata (axi_sync_w.data), 106 | .s_axi_wstrb (axi_sync_w.strb), 107 | .m_axi_aclk (io_clk_i), 108 | .m_axi_aresetn (rst_ni), 109 | .m_axi_arready (axi_ar_ready), 110 | .m_axi_arvalid (axi_ar_valid), 111 | .m_axi_araddr (axi_ar.addr), 112 | .m_axi_arprot (axi_ar.prot), 113 | .m_axi_awready (axi_aw_ready), 114 | .m_axi_awvalid (axi_aw_valid), 115 | .m_axi_awaddr (axi_aw.addr), 116 | .m_axi_awprot (axi_aw.prot), 117 | .m_axi_bready (axi_b_ready), 118 | .m_axi_bvalid (axi_b_valid), 119 | .m_axi_bresp (axi_b.resp), 120 | .m_axi_rready (axi_r_ready), 121 | .m_axi_rvalid (axi_r_valid), 122 | .m_axi_rdata (axi_r.data), 123 | .m_axi_rresp (axi_r.resp), 124 | .m_axi_wready (axi_w_ready), 125 | .m_axi_wvalid (axi_w_valid), 126 | .m_axi_wdata (axi_w.data), 127 | .m_axi_wstrb (axi_w.strb) 128 | ); 129 | 130 | // TileLink to AXI-lite bridge 131 | 132 | tl_axi_lite_adapter #( 133 | .DataWidth (DataWidth), 134 | .AddrWidth (AddrWidth), 135 | .SourceWidth (SourceWidth) 136 | ) adapter ( 137 | .clk_i (clk_i), 138 | .rst_ni (rst_ni), 139 | `TL_FORWARD_DEVICE_PORT(host, link), 140 | `AXI_CONNECT_HOST_PORT(device, axi_sync) 141 | ); 142 | 143 | endmodule 144 | -------------------------------------------------------------------------------- /util/program_flash.tcl: -------------------------------------------------------------------------------- 1 | if {[info exists env(PART)]} { 2 | set part $env(PART) 3 | } else { 4 | puts "ERROR: FPGA part not specified" 5 | exit 1 6 | } 7 | 8 | if {[info exists env(CFGMEM_PART)]} { 9 | set cfgmem_part $env(CFGMEM_PART) 10 | } else { 11 | puts "ERROR: Flash memory part not specified" 12 | exit 1 13 | } 14 | 15 | if {[info exists env(PROGRAM)]} { 16 | set program $env(PROGRAM) 17 | } else { 18 | puts "ERROR: MCS file not specified" 19 | exit 1 20 | } 21 | 22 | # Connect to Xilinx Hardware Server 23 | if { [ catch { open_hw_manager } ] } { open_hw } 24 | 25 | if {[info exists env(HW_SERVER)]} { 26 | connect_hw_server -url $env(HW_SERVER) -allow_non_jtag 27 | } else { 28 | connect_hw_server 29 | } 30 | 31 | set hw_targets [get_hw_targets] 32 | 33 | if { [llength $hw_targets] == 0 } { 34 | puts "ERROR: Failed to find any targets" 35 | exit 1 36 | } 37 | 38 | # Find the first target and device that contains a FPGA $part. 39 | set hw_device_found 0 40 | foreach hw_target $hw_targets { 41 | puts "NFO: Trying to use hardware target $hw_target" 42 | current_hw_target $hw_target 43 | 44 | # Open hardware target 45 | # The Vivado hardware server isn't always able to reliably open a target. 46 | # Try three times before giving up. 47 | set hw_target_opened 0 48 | for {set open_hw_target_try 1} {$open_hw_target_try <= 3} {incr open_hw_target_try} { 49 | if {[catch {open_hw_target} res_open_hw_target] == 0} { 50 | set hw_target_opened 1 51 | break 52 | } 53 | } 54 | if { $hw_target_opened == 0 } { 55 | puts "WARNING: Unable to open hardware target $hw_target after " \ 56 | "$open_hw_target_try tries. Skipping." 57 | continue 58 | } 59 | puts "INFO: Opened hardware target $hw_target on try $open_hw_target_try." 60 | 61 | # Iterate through all devices and find one which contains $part 62 | foreach { hw_device } [get_hw_devices] { 63 | if { [string first [get_property PART $hw_device] $part] == 0 } { 64 | puts "INFO: Found $part as part of $hw_device." 65 | current_hw_device $hw_device 66 | set hw_device_found 1 67 | break 68 | } 69 | } 70 | 71 | if { $hw_device_found == 1 } { 72 | break 73 | } else { 74 | # Close currently tried device, and try with next one. 75 | puts "INFO: Part not found as part of $hw_target. Trying next device." 76 | close_hw_target 77 | } 78 | } 79 | if { $hw_device_found == 0 } { 80 | puts "ERROR: None of the hardware targets included a $part FPGA part. \ 81 | Check cables and ensure that jumpers are correct for JTAG programming." 82 | exit 1 83 | } 84 | puts "INFO: Programming flash to device $hw_device on target $hw_target." 85 | 86 | current_hw_device $hw_device 87 | set_property PARAM.FREQUENCY 15000000 [get_hw_targets $hw_target] 88 | 89 | create_hw_cfgmem -hw_device [current_hw_device] [lindex [get_cfgmem_parts $cfgmem_part] 0] 90 | set_property PROGRAM.ADDRESS_RANGE {use_file} [get_property PROGRAM.HW_CFGMEM [current_hw_device]] 91 | set_property PROGRAM.FILES [list $program] [get_property PROGRAM.HW_CFGMEM [current_hw_device]] 92 | set_property PROGRAM.PRM_FILE {} [get_property PROGRAM.HW_CFGMEM [current_hw_device]] 93 | set_property PROGRAM.UNUSED_PIN_TERMINATION {pull-none} [get_property PROGRAM.HW_CFGMEM [current_hw_device]] 94 | set_property PROGRAM.BLANK_CHECK 0 [get_property PROGRAM.HW_CFGMEM [current_hw_device]] 95 | set_property PROGRAM.ERASE 1 [get_property PROGRAM.HW_CFGMEM [current_hw_device]] 96 | set_property PROGRAM.CFG_PROGRAM 1 [get_property PROGRAM.HW_CFGMEM [current_hw_device]] 97 | set_property PROGRAM.VERIFY 1 [get_property PROGRAM.HW_CFGMEM [current_hw_device]] 98 | set_property PROGRAM.CHECKSUM 0 [get_property PROGRAM.HW_CFGMEM [current_hw_device]] 99 | startgroup 100 | create_hw_bitstream -hw_device [current_hw_device] [get_property PROGRAM.HW_CFGMEM_BITFILE [current_hw_device]] 101 | program_hw_devices [current_hw_device] 102 | refresh_hw_device [current_hw_device] 103 | program_hw_cfgmem -hw_cfgmem [get_property PROGRAM.HW_CFGMEM [current_hw_device]] 104 | endgroup 105 | 106 | # Disconnect from Xilinx Hardware Server 107 | close_hw_target 108 | disconnect_hw_server 109 | 110 | puts "" 111 | puts "INFO: SUCCESS! Flash has been programmed with firmware" 112 | -------------------------------------------------------------------------------- /genesys_2/data/device_tree.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | model = "riscv-virtio,qemu"; 5 | compatible = "riscv-virtio"; 6 | #address-cells = <0x2>; 7 | #size-cells = <0x2>; 8 | 9 | chosen { 10 | bootargs = "console=ttyS0,230400 ro root=/dev/mmcblk0 rootflags=noload init=/bin/bash rcupdate.rcu_cpu_stall_suppress=1 rootwait"; 11 | }; 12 | 13 | cpus { 14 | timebase-frequency = <1000000>; 15 | #address-cells = <0x1>; 16 | #size-cells = <0x0>; 17 | 18 | cpu-map { 19 | cluster0 { 20 | core0 { 21 | cpu = <&CPU0>; 22 | }; 23 | core1 { 24 | cpu = <&CPU1>; 25 | }; 26 | }; 27 | }; 28 | 29 | CPU0: cpu@0 { 30 | clock-frequency = <0x0>; 31 | mmu-type = "riscv,sv39"; 32 | riscv,isa = "rv64imafdc"; 33 | compatible = "riscv"; 34 | status = "okay"; 35 | reg = <0x0>; 36 | device_type = "cpu"; 37 | 38 | cpu0_intc: interrupt-controller { 39 | #interrupt-cells = <0x1>; 40 | interrupt-controller; 41 | compatible = "riscv,cpu-intc"; 42 | }; 43 | }; 44 | 45 | CPU1: cpu@1 { 46 | clock-frequency = <0x0>; 47 | mmu-type = "riscv,sv39"; 48 | riscv,isa = "rv64imafdc"; 49 | compatible = "riscv"; 50 | status = "okay"; 51 | reg = <0x1>; 52 | device_type = "cpu"; 53 | 54 | cpu1_intc: interrupt-controller { 55 | #interrupt-cells = <0x1>; 56 | interrupt-controller; 57 | compatible = "riscv,cpu-intc"; 58 | }; 59 | }; 60 | }; 61 | 62 | clk: clock { 63 | compatible = "fixed-clock"; 64 | #clock-cells = <0>; 65 | clock-frequency = <50000000>; 66 | }; 67 | 68 | soc { 69 | ranges; 70 | compatible = "simple-bus"; 71 | #address-cells = <0x2>; 72 | #size-cells = <0x2>; 73 | 74 | plic: plic@11000000 { 75 | #interrupt-cells = <0x1>; 76 | interrupt-controller; 77 | compatible = "sifive,plic-1.0.0"; 78 | riscv,ndev = <0x1f>; 79 | reg = <0x0 0x11000000 0x0 0x400000>; 80 | interrupts-extended = <&cpu0_intc 0x9 &cpu1_intc 0x9>; 81 | }; 82 | 83 | clint@11400000 { 84 | compatible = "sifive,clint0"; 85 | reg = /bits/ 64 <0x11400000 0x10000>; 86 | }; 87 | 88 | serial@10000000 { 89 | clock-frequency = <18432000>; 90 | compatible = "ns16550a"; 91 | current-speed = <230400>; 92 | interrupts-extended = <&plic 0x1>; 93 | reg = <0 0x10000000 0x0 0x2000>; 94 | reg-offset = <0x1000>; 95 | reg-shift = <2>; 96 | reg-io-width = <4>; 97 | }; 98 | 99 | mmc@10010000 { 100 | compatible = "garyguo,sdhci"; 101 | reg = <0x00 0x10010000 0x00 0x1000>; 102 | interrupts-extended = <&plic 0x2>; 103 | }; 104 | 105 | ethernet@10100000 { 106 | compatible = "xlnx,axi-ethernet-1.00.a"; 107 | device_type = "network"; 108 | interrupt-parent = <&plic>; 109 | interrupts = <4 5 3>; 110 | phy-mode = "rgmii"; 111 | clocks = <&clk>; 112 | reg = /bits/ 64 <0x10100000 0x40000 0x10140000 0x10000>; 113 | xlnx,rxcsum = <2>; 114 | xlnx,rxmem = <0x1000>; 115 | xlnx,txcsum = <2>; 116 | phy-handle = <&phy1>; 117 | local-mac-address = [00 00 00 00 00 00]; 118 | mdio { 119 | #address-cells = <1>; 120 | #size-cells = <0>; 121 | phy1: phy@1 { 122 | compatible = "ethernet-phy-id001c.c915"; 123 | device_type = "ethernet-phy"; 124 | reg = <1>; 125 | interrupt-parent = <&plic>; 126 | interrupts = <6>; 127 | }; 128 | }; 129 | }; 130 | }; 131 | 132 | memory@40000000 { 133 | reg = /bits/ 64 <0x40000000 0x40000000>; 134 | device_type = "memory"; 135 | }; 136 | }; 137 | -------------------------------------------------------------------------------- /nexys_a7/data/device_tree.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | model = "riscv-virtio,qemu"; 5 | compatible = "riscv-virtio"; 6 | #address-cells = <0x2>; 7 | #size-cells = <0x2>; 8 | 9 | chosen { 10 | bootargs = "console=ttyS0,230400 ro root=/dev/mmcblk0 rootflags=noload init=/bin/bash rcupdate.rcu_cpu_stall_suppress=1 rootwait"; 11 | }; 12 | 13 | cpus { 14 | timebase-frequency = <1000000>; 15 | #address-cells = <0x1>; 16 | #size-cells = <0x0>; 17 | 18 | cpu-map { 19 | cluster0 { 20 | core0 { 21 | cpu = <&CPU0>; 22 | }; 23 | core1 { 24 | cpu = <&CPU1>; 25 | }; 26 | }; 27 | }; 28 | 29 | CPU0: cpu@0 { 30 | clock-frequency = <0x0>; 31 | mmu-type = "riscv,sv39"; 32 | riscv,isa = "rv64imafdc"; 33 | compatible = "riscv"; 34 | status = "okay"; 35 | reg = <0x0>; 36 | device_type = "cpu"; 37 | 38 | cpu0_intc: interrupt-controller { 39 | #interrupt-cells = <0x1>; 40 | interrupt-controller; 41 | compatible = "riscv,cpu-intc"; 42 | }; 43 | }; 44 | 45 | CPU1: cpu@1 { 46 | clock-frequency = <0x0>; 47 | mmu-type = "riscv,sv39"; 48 | riscv,isa = "rv64imafdc"; 49 | compatible = "riscv"; 50 | status = "okay"; 51 | reg = <0x1>; 52 | device_type = "cpu"; 53 | 54 | cpu1_intc: interrupt-controller { 55 | #interrupt-cells = <0x1>; 56 | interrupt-controller; 57 | compatible = "riscv,cpu-intc"; 58 | }; 59 | }; 60 | }; 61 | 62 | clk: clock { 63 | compatible = "fixed-clock"; 64 | #clock-cells = <0>; 65 | clock-frequency = <50000000>; 66 | }; 67 | 68 | soc { 69 | ranges; 70 | compatible = "simple-bus"; 71 | #address-cells = <0x2>; 72 | #size-cells = <0x2>; 73 | 74 | plic: plic@11000000 { 75 | #interrupt-cells = <0x1>; 76 | interrupt-controller; 77 | compatible = "sifive,plic-1.0.0"; 78 | riscv,ndev = <0x1f>; 79 | reg = <0x0 0x11000000 0x0 0x400000>; 80 | interrupts-extended = <&cpu0_intc 0x9 &cpu1_intc 0x9>; 81 | }; 82 | 83 | clint@11400000 { 84 | compatible = "sifive,clint0"; 85 | reg = /bits/ 64 <0x11400000 0x10000>; 86 | }; 87 | 88 | serial@10000000 { 89 | clock-frequency = <18432000>; 90 | compatible = "ns16550a"; 91 | current-speed = <230400>; 92 | interrupts-extended = <&plic 0x1>; 93 | reg = <0 0x10000000 0x0 0x2000>; 94 | reg-offset = <0x1000>; 95 | reg-shift = <2>; 96 | reg-io-width = <4>; 97 | }; 98 | 99 | mmc@10010000 { 100 | compatible = "garyguo,sdhci"; 101 | reg = <0x00 0x10010000 0x00 0x1000>; 102 | interrupts-extended = <&plic 0x2>; 103 | }; 104 | 105 | ethernet@10100000 { 106 | compatible = "xlnx,axi-ethernet-1.00.a"; 107 | device_type = "network"; 108 | interrupt-parent = <&plic>; 109 | interrupts = <4 5 3>; 110 | phy-mode = "mii"; 111 | clocks = <&clk>; 112 | reg = /bits/ 64 <0x10100000 0x40000 0x10140000 0x10000>; 113 | xlnx,rxcsum = <2>; 114 | xlnx,rxmem = <0x1000>; 115 | xlnx,txcsum = <2>; 116 | phy-handle = <&phy1>; 117 | local-mac-address = [00 00 00 00 00 00]; 118 | mdio { 119 | #address-cells = <1>; 120 | #size-cells = <0>; 121 | phy1: phy@1 { 122 | compatible = "ethernet-phy-id0007.c0f0"; 123 | device_type = "ethernet-phy"; 124 | reg = <1>; 125 | interrupt-parent = <&plic>; 126 | interrupts = <6>; 127 | }; 128 | }; 129 | }; 130 | }; 131 | 132 | memory@40000000 { 133 | reg = /bits/ 64 <0x40000000 0x08000000>; 134 | device_type = "memory"; 135 | }; 136 | }; 137 | -------------------------------------------------------------------------------- /nexys_video/data/device_tree.dts: -------------------------------------------------------------------------------- 1 | /dts-v1/; 2 | 3 | / { 4 | model = "riscv-virtio,qemu"; 5 | compatible = "riscv-virtio"; 6 | #address-cells = <0x2>; 7 | #size-cells = <0x2>; 8 | 9 | chosen { 10 | ranges; 11 | #address-cells = <0x2>; 12 | #size-cells = <0x2>; 13 | 14 | bootargs = "console=ttyS0,230400 ro root=/dev/mmcblk0 rootflags=noload init=/bin/bash rcupdate.rcu_cpu_stall_suppress=1 rootwait"; 15 | 16 | framebuffer0: framebuffer@5fc00000 { 17 | compatible = "simple-framebuffer"; 18 | reg = /bits/ 64 <0x5fc00000 0x200000>; 19 | width = <1280>; 20 | height = <720>; 21 | stride = <(1280 * 2)>; 22 | format = "r5g6b5"; 23 | status = "okay"; 24 | }; 25 | }; 26 | 27 | cpus { 28 | timebase-frequency = <1000000>; 29 | #address-cells = <0x1>; 30 | #size-cells = <0x0>; 31 | 32 | cpu-map { 33 | cluster0 { 34 | core0 { 35 | cpu = <&CPU0>; 36 | }; 37 | core1 { 38 | cpu = <&CPU1>; 39 | }; 40 | }; 41 | }; 42 | 43 | CPU0: cpu@0 { 44 | clock-frequency = <0x0>; 45 | mmu-type = "riscv,sv39"; 46 | riscv,isa = "rv64imafdc"; 47 | compatible = "riscv"; 48 | status = "okay"; 49 | reg = <0x0>; 50 | device_type = "cpu"; 51 | 52 | cpu0_intc: interrupt-controller { 53 | #interrupt-cells = <0x1>; 54 | interrupt-controller; 55 | compatible = "riscv,cpu-intc"; 56 | }; 57 | }; 58 | 59 | CPU1: cpu@1 { 60 | clock-frequency = <0x0>; 61 | mmu-type = "riscv,sv39"; 62 | riscv,isa = "rv64imafdc"; 63 | compatible = "riscv"; 64 | status = "okay"; 65 | reg = <0x1>; 66 | device_type = "cpu"; 67 | 68 | cpu1_intc: interrupt-controller { 69 | #interrupt-cells = <0x1>; 70 | interrupt-controller; 71 | compatible = "riscv,cpu-intc"; 72 | }; 73 | }; 74 | }; 75 | 76 | clk: clock { 77 | compatible = "fixed-clock"; 78 | #clock-cells = <0>; 79 | clock-frequency = <50000000>; 80 | }; 81 | 82 | soc { 83 | ranges; 84 | compatible = "simple-bus"; 85 | #address-cells = <0x2>; 86 | #size-cells = <0x2>; 87 | 88 | plic: plic@11000000 { 89 | #interrupt-cells = <0x1>; 90 | interrupt-controller; 91 | compatible = "sifive,plic-1.0.0"; 92 | riscv,ndev = <0x1f>; 93 | reg = <0x0 0x11000000 0x0 0x400000>; 94 | interrupts-extended = <&cpu0_intc 0x9 &cpu1_intc 0x9>; 95 | }; 96 | 97 | clint@11400000 { 98 | compatible = "sifive,clint0"; 99 | reg = /bits/ 64 <0x11400000 0x10000>; 100 | }; 101 | 102 | serial@10000000 { 103 | clock-frequency = <18432000>; 104 | compatible = "ns16550a"; 105 | current-speed = <230400>; 106 | interrupts-extended = <&plic 0x1>; 107 | reg = <0 0x10000000 0x0 0x2000>; 108 | reg-offset = <0x1000>; 109 | reg-shift = <2>; 110 | reg-io-width = <4>; 111 | }; 112 | 113 | ps2@10030000 { 114 | compatible = "digilent,axi-ps2-1.0"; 115 | reg = /bits/ 64 <0x10030000 0x1000>; 116 | interrupts-extended = <&plic 0x7>; 117 | }; 118 | 119 | mmc@10010000 { 120 | compatible = "garyguo,sdhci"; 121 | reg = <0x00 0x10010000 0x00 0x1000>; 122 | interrupts-extended = <&plic 0x2>; 123 | }; 124 | 125 | display@10020000 { 126 | compatible = "garyguo,display-controller"; 127 | reg = /bits/ 64 <0x10020000 0x1000>; 128 | }; 129 | }; 130 | 131 | memory@40000000 { 132 | reg = /bits/ 64 <0x40000000 0x20000000>; 133 | device_type = "memory"; 134 | }; 135 | 136 | reserved-memory { 137 | #address-cells = <2>; 138 | #size-cells = <2>; 139 | ranges; 140 | 141 | framebuffer@5fc00000 { 142 | reg = /bits/ 64 <0x5fc00000 0x200000>; 143 | }; 144 | 145 | firmware@5fe00000 { 146 | no-map; 147 | reg = /bits/ 64 <0x5fe00000 0x200000>; 148 | }; 149 | }; 150 | }; 151 | -------------------------------------------------------------------------------- /tools/linker/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "adler" 7 | version = "1.0.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 10 | 11 | [[package]] 12 | name = "byteorder" 13 | version = "1.4.3" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 16 | 17 | [[package]] 18 | name = "cfg-if" 19 | version = "1.0.0" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 22 | 23 | [[package]] 24 | name = "crc32fast" 25 | version = "1.3.2" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" 28 | dependencies = [ 29 | "cfg-if", 30 | ] 31 | 32 | [[package]] 33 | name = "flate2" 34 | version = "1.0.26" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" 37 | dependencies = [ 38 | "crc32fast", 39 | "miniz_oxide", 40 | ] 41 | 42 | [[package]] 43 | name = "linker" 44 | version = "0.1.0" 45 | dependencies = [ 46 | "object", 47 | ] 48 | 49 | [[package]] 50 | name = "memchr" 51 | version = "2.5.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" 54 | 55 | [[package]] 56 | name = "miniz_oxide" 57 | version = "0.7.1" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" 60 | dependencies = [ 61 | "adler", 62 | ] 63 | 64 | [[package]] 65 | name = "object" 66 | version = "0.31.1" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" 69 | dependencies = [ 70 | "flate2", 71 | "memchr", 72 | "ruzstd", 73 | ] 74 | 75 | [[package]] 76 | name = "proc-macro2" 77 | version = "1.0.64" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da" 80 | dependencies = [ 81 | "unicode-ident", 82 | ] 83 | 84 | [[package]] 85 | name = "quote" 86 | version = "1.0.29" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "573015e8ab27661678357f27dc26460738fd2b6c86e46f386fde94cb5d913105" 89 | dependencies = [ 90 | "proc-macro2", 91 | ] 92 | 93 | [[package]] 94 | name = "ruzstd" 95 | version = "0.3.1" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "9a15e661f0f9dac21f3494fe5d23a6338c0ac116a2d22c2b63010acd89467ffe" 98 | dependencies = [ 99 | "byteorder", 100 | "thiserror", 101 | "twox-hash", 102 | ] 103 | 104 | [[package]] 105 | name = "static_assertions" 106 | version = "1.1.0" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 109 | 110 | [[package]] 111 | name = "syn" 112 | version = "2.0.25" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "15e3fc8c0c74267e2df136e5e5fb656a464158aa57624053375eb9c8c6e25ae2" 115 | dependencies = [ 116 | "proc-macro2", 117 | "quote", 118 | "unicode-ident", 119 | ] 120 | 121 | [[package]] 122 | name = "thiserror" 123 | version = "1.0.43" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" 126 | dependencies = [ 127 | "thiserror-impl", 128 | ] 129 | 130 | [[package]] 131 | name = "thiserror-impl" 132 | version = "1.0.43" 133 | source = "registry+https://github.com/rust-lang/crates.io-index" 134 | checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" 135 | dependencies = [ 136 | "proc-macro2", 137 | "quote", 138 | "syn", 139 | ] 140 | 141 | [[package]] 142 | name = "twox-hash" 143 | version = "1.6.3" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" 146 | dependencies = [ 147 | "cfg-if", 148 | "static_assertions", 149 | ] 150 | 151 | [[package]] 152 | name = "unicode-ident" 153 | version = "1.0.10" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "22049a19f4a68748a168c0fc439f9516686aa045927ff767eca0a85101fb6e73" 156 | -------------------------------------------------------------------------------- /firmware/src/elf.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types)] 2 | #![allow(unused)] 3 | 4 | type Elf64_Half = u16; 5 | type Elf64_Word = u32; 6 | type Elf64_Off = u64; 7 | type Elf64_Addr = u64; 8 | type Elf64_Xword = u64; 9 | type Elf64_Sxword = i64; 10 | 11 | const PT_LOAD: u32 = 1; 12 | 13 | #[repr(C)] 14 | struct Elf64_Ehdr { 15 | pub e_ident: [u8; 16], 16 | pub e_type: Elf64_Half, 17 | pub e_machine: Elf64_Half, 18 | pub e_version: Elf64_Word, 19 | pub e_entry: Elf64_Addr, 20 | pub e_phoff: Elf64_Off, 21 | pub e_shoff: Elf64_Off, 22 | pub e_flags: Elf64_Word, 23 | pub e_ehsize: Elf64_Half, 24 | pub e_phentsize: Elf64_Half, 25 | pub e_phnum: Elf64_Half, 26 | pub e_shentsize: Elf64_Half, 27 | pub e_shnum: Elf64_Half, 28 | pub e_shstrndx: Elf64_Half, 29 | } 30 | 31 | #[repr(C)] 32 | struct Elf64_Phdr { 33 | pub p_type: Elf64_Word, 34 | pub p_flags: Elf64_Word, 35 | pub p_offset: Elf64_Off, 36 | pub p_vaddr: Elf64_Addr, 37 | pub p_paddr: Elf64_Addr, 38 | pub p_filesz: Elf64_Xword, 39 | pub p_memsz: Elf64_Xword, 40 | pub p_align: Elf64_Xword, 41 | } 42 | 43 | const PF_R: u32 = 0x4; 44 | const PF_W: u32 = 0x2; 45 | const PF_X: u32 = 0x1; 46 | const ET_EXEC: Elf64_Half = 2; 47 | const ET_DYN: Elf64_Half = 3; 48 | const EM_RISCV: Elf64_Half = 243; 49 | 50 | #[repr(C)] 51 | struct Loader { 52 | memory: *const u8, 53 | } 54 | 55 | struct PhdrIter<'a> { 56 | i: usize, 57 | ehdr: &'a Elf64_Ehdr, 58 | } 59 | 60 | impl<'a> Iterator for PhdrIter<'a> { 61 | type Item = &'a Elf64_Phdr; 62 | fn next(&mut self) -> Option { 63 | if self.i == self.ehdr.e_phnum as usize { 64 | None 65 | } else { 66 | let ptr = self.ehdr as *const _ as usize 67 | + self.ehdr.e_phoff as usize 68 | + self.ehdr.e_phentsize as usize * self.i; 69 | self.i += 1; 70 | Some(unsafe { &*(ptr as *const Elf64_Phdr) }) 71 | } 72 | } 73 | } 74 | 75 | impl Loader { 76 | fn ehdr(&self) -> &Elf64_Ehdr { 77 | unsafe { &*(self.memory as *const Elf64_Ehdr) } 78 | } 79 | 80 | fn phdr(&self) -> PhdrIter { 81 | PhdrIter { 82 | i: 0, 83 | ehdr: self.ehdr(), 84 | } 85 | } 86 | 87 | pub fn new(ptr: *const u8) -> Loader { 88 | // Must be properly aligned. 89 | assert!(ptr as usize % 8 == 0); 90 | Loader { memory: ptr } 91 | } 92 | 93 | unsafe fn load_kernel(&self, load_addr: u64) -> u64 { 94 | let header = self.ehdr(); 95 | 96 | // Check the ELF magic numbers 97 | if &header.e_ident[0..4] != "\x7FELF".as_bytes() { 98 | panic!("Not ELF file") 99 | } 100 | 101 | // We can only proceed with executable or dynamic binary. 102 | if header.e_type != ET_EXEC && header.e_type != ET_DYN { 103 | panic!("the binary is not executable."); 104 | } 105 | 106 | // Check that the ELF is for RISC-V 107 | if header.e_machine != EM_RISCV { 108 | panic!("the binary is not for RISC-V."); 109 | } 110 | 111 | // Scan the bounds of the image. 112 | let mut loaddr = u64::max_value(); 113 | let mut hiaddr = 0; 114 | for h in self.phdr() { 115 | if h.p_type == PT_LOAD { 116 | loaddr = core::cmp::min(loaddr, h.p_vaddr); 117 | hiaddr = core::cmp::max(hiaddr, h.p_vaddr + h.p_memsz); 118 | } 119 | } 120 | 121 | loaddr &= !4095; 122 | hiaddr = (hiaddr + 4095) & !4095; 123 | 124 | for h in self.phdr() { 125 | if h.p_type == PT_LOAD { 126 | // size in memory cannot be smaller than size in file 127 | if h.p_filesz > h.p_memsz { 128 | panic!("invalid elf file: constraint p_filesz <= p_memsz is not satisified"); 129 | } 130 | 131 | // Copy across. 132 | core::ptr::copy_nonoverlapping( 133 | (self.memory as usize + h.p_offset as usize) as *const u8, 134 | (h.p_vaddr - loaddr + load_addr) as usize as *mut u8, 135 | h.p_filesz as usize, 136 | ); 137 | 138 | // Zero-out the rest 139 | core::ptr::write_bytes( 140 | (h.p_vaddr + h.p_filesz - loaddr + load_addr) as usize as *mut u8, 141 | 0, 142 | (h.p_memsz - h.p_filesz) as usize, 143 | ); 144 | } 145 | } 146 | 147 | hiaddr - loaddr 148 | } 149 | } 150 | 151 | pub unsafe fn load_elf(slice: &[u8], addr: usize) -> usize { 152 | Loader::new(slice.as_ptr()).load_kernel(addr as _) as _ 153 | } 154 | -------------------------------------------------------------------------------- /firmware/src/ipi.rs: -------------------------------------------------------------------------------- 1 | use core::arch::asm; 2 | use core::sync::atomic::{AtomicU32, AtomicUsize, Ordering}; 3 | use spin::Mutex; 4 | 5 | use super::address::{CLINT_BASE, MAX_HART_COUNT}; 6 | use crate::hart_mask::HartMask; 7 | 8 | static HART_COUNT: AtomicUsize = AtomicUsize::new(0); 9 | 10 | pub fn hart_count() -> usize { 11 | HART_COUNT.load(Ordering::Relaxed) 12 | } 13 | 14 | fn probe_hart(hart: usize) -> bool { 15 | unsafe { 16 | let ptr = (CLINT_BASE + 0x4000 + hart * 8) as *mut u64; 17 | // Write a value to MTIMECMP and read back to see if it actually exists. 18 | core::ptr::write_volatile(ptr, u64::MAX - 1); 19 | if core::ptr::read_volatile(ptr) != u64::MAX - 1 { 20 | return false; 21 | } 22 | core::ptr::write_volatile(ptr, u64::MAX); 23 | if core::ptr::read_volatile(ptr) != u64::MAX { 24 | return false; 25 | } 26 | true 27 | } 28 | } 29 | 30 | pub fn probe_hart_count() { 31 | let count = (1..MAX_HART_COUNT) 32 | .find(|&i| !probe_hart(i)) 33 | .unwrap_or(MAX_HART_COUNT); 34 | info!("{} cores probed from CLINT", count); 35 | HART_COUNT.store(count, Ordering::Relaxed); 36 | } 37 | 38 | pub fn set_msip(hart_id: usize, value: bool) { 39 | assert!(hart_id < hart_count()); 40 | unsafe { 41 | core::ptr::write_volatile((CLINT_BASE + hart_id * 4) as *mut u32, value as u32); 42 | } 43 | } 44 | 45 | fn cpu_relax() { 46 | unsafe { 47 | asm!("nop; nop; nop", options(nomem, nostack)); 48 | } 49 | } 50 | 51 | fn disable_irq() { 52 | unsafe { 53 | asm!("csrc mstatus, 8", options(nomem, nostack)); 54 | } 55 | core::sync::atomic::compiler_fence(Ordering::Acquire); 56 | } 57 | 58 | fn enable_irq() { 59 | core::sync::atomic::compiler_fence(Ordering::Release); 60 | unsafe { 61 | asm!("csrs mstatus, 8", options(nomem, nostack)); 62 | } 63 | } 64 | 65 | fn run_on_hart_common( 66 | mask: HartMask, 67 | f: &'static (dyn Fn() + Sync), 68 | wait: Option<&'static AtomicU32>, 69 | ) -> u32 { 70 | let cur_id = super::hartid(); 71 | let mut wait_num = 0; 72 | for hart_id in 0..hart_count() { 73 | // Check the mask to determine if we need to run on this hart 74 | if hart_id == cur_id || !mask.is_set(hart_id) { 75 | continue; 76 | } 77 | 78 | wait_num += 1; 79 | 80 | // Set IPI_DATA 81 | loop { 82 | let mut guard = IPI_DATA[hart_id].lock(); 83 | if guard.is_none() { 84 | *guard = Some(IpiData { func: f, wait }); 85 | break; 86 | } 87 | // Some one has already send a IPI to this hart and it hasn't been processed yet. 88 | // Release the lock and wait for it to be cleared. 89 | drop(guard); 90 | 91 | // We expect the other hart to handle this relatively quickly, so busy wait. 92 | // Enable interrupt so we can deal with IPIs send to us 93 | enable_irq(); 94 | cpu_relax(); 95 | disable_irq(); 96 | } 97 | 98 | // Kick the hart 99 | set_msip(hart_id, true); 100 | } 101 | 102 | if mask.is_set(cur_id) { 103 | f(); 104 | } 105 | 106 | wait_num 107 | } 108 | 109 | pub fn run_on_hart(mask: HartMask, f: &'static (dyn Fn() + Sync)) { 110 | run_on_hart_common(mask, f, None); 111 | } 112 | 113 | pub fn run_on_hart_wait(mask: HartMask, f: &(dyn Fn() + Sync)) { 114 | let wait = AtomicU32::new(0); 115 | 116 | // This is okay, because we will wait for the call to complete. By the time wait is completed 117 | // we would have no copies of `f`, so this lifetime transmute is safe. 118 | let wait_num = run_on_hart_common( 119 | mask, 120 | unsafe { core::mem::transmute(f) }, 121 | Some(unsafe { core::mem::transmute(&wait) }), 122 | ); 123 | 124 | // Wait for the response of the ACK. 125 | loop { 126 | if wait.load(Ordering::Acquire) == wait_num { 127 | break; 128 | } 129 | enable_irq(); 130 | cpu_relax(); 131 | disable_irq(); 132 | } 133 | } 134 | 135 | struct IpiData { 136 | func: &'static (dyn Fn() + Sync), 137 | wait: Option<&'static AtomicU32>, 138 | } 139 | 140 | static IPI_DATA: [Mutex>; MAX_HART_COUNT] = { 141 | const INIT: Mutex> = Mutex::new(None); 142 | [INIT; MAX_HART_COUNT] 143 | }; 144 | 145 | pub fn process_ipi() { 146 | let cur_id = super::hartid(); 147 | set_msip(cur_id, false); 148 | 149 | let mut guard = IPI_DATA[cur_id].lock(); 150 | if let Some(data) = guard.take() { 151 | (data.func)(); 152 | if let Some(wait) = data.wait { 153 | wait.fetch_add(1, Ordering::Release); 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /nexys_video/rtl/ddr.sv: -------------------------------------------------------------------------------- 1 | `include "axi_util.svh" 2 | `include "tl_util.svh" 3 | 4 | module ddr #( 5 | parameter DataWidth = 128, 6 | parameter AddrWidth = 29, 7 | parameter SourceWidth = 8 8 | ) ( 9 | // Clock and reset 10 | input sys_clk_i, 11 | input sys_rst_ni, 12 | 13 | output clk_o, 14 | output io_clk_o, 15 | output rst_no, 16 | 17 | // IO port 18 | output [14:0] ddr3_sdram_addr, 19 | output [2:0] ddr3_sdram_ba, 20 | output ddr3_sdram_cas_n, 21 | output [0:0] ddr3_sdram_ck_n, 22 | output [0:0] ddr3_sdram_ck_p, 23 | output [0:0] ddr3_sdram_cke, 24 | output [1:0] ddr3_sdram_dm, 25 | inout [15:0] ddr3_sdram_dq, 26 | inout [1:0] ddr3_sdram_dqs_n, 27 | inout [1:0] ddr3_sdram_dqs_p, 28 | output [0:0] ddr3_sdram_odt, 29 | output ddr3_sdram_ras_n, 30 | output ddr3_sdram_reset_n, 31 | output ddr3_sdram_we_n, 32 | 33 | // TileLink port 34 | `TL_DECLARE_DEVICE_PORT(DataWidth, AddrWidth, SourceWidth, 1, link) 35 | ); 36 | 37 | `AXI_DECLARE(DataWidth, AddrWidth, SourceWidth, axi); 38 | 39 | wire ref_clk; 40 | wire mig_clk; 41 | 42 | mig_7series_0 ddr_ctrl ( 43 | // Clock 44 | .sys_clk_i (sys_clk_i), 45 | .clk_ref_i (ref_clk), 46 | .sys_rst (sys_rst_ni), 47 | .ui_clk (mig_clk), 48 | .ui_clk_sync_rst (), 49 | .ui_addn_clk_0 (ref_clk), 50 | .ui_addn_clk_1 (clk_o), 51 | .ui_addn_clk_2 (io_clk_o), 52 | .ui_addn_clk_3 (), 53 | .ui_addn_clk_4 (), 54 | .mmcm_locked (rst_no), 55 | // DDR interface 56 | .ddr3_addr (ddr3_sdram_addr), 57 | .ddr3_ba (ddr3_sdram_ba), 58 | .ddr3_cas_n (ddr3_sdram_cas_n), 59 | .ddr3_ck_n (ddr3_sdram_ck_n), 60 | .ddr3_ck_p (ddr3_sdram_ck_p), 61 | .ddr3_cke (ddr3_sdram_cke), 62 | .ddr3_dm (ddr3_sdram_dm), 63 | .ddr3_dq (ddr3_sdram_dq), 64 | .ddr3_dqs_n (ddr3_sdram_dqs_n), 65 | .ddr3_dqs_p (ddr3_sdram_dqs_p), 66 | .ddr3_odt (ddr3_sdram_odt), 67 | .ddr3_ras_n (ddr3_sdram_ras_n), 68 | .ddr3_reset_n (ddr3_sdram_reset_n), 69 | .ddr3_we_n (ddr3_sdram_we_n), 70 | // AXI interface 71 | .aresetn (rst_no), 72 | .s_axi_arready (axi_ar_ready), 73 | .s_axi_arvalid (axi_ar_valid), 74 | .s_axi_araddr (axi_ar.addr), 75 | .s_axi_arburst (axi_ar.burst), 76 | .s_axi_arcache (axi_ar.cache), 77 | .s_axi_arid (axi_ar.id), 78 | .s_axi_arlen (axi_ar.len), 79 | .s_axi_arlock (axi_ar.lock), 80 | .s_axi_arprot (axi_ar.prot), 81 | .s_axi_arqos (axi_ar.qos), 82 | .s_axi_arsize (axi_ar.size), 83 | .s_axi_awready (axi_aw_ready), 84 | .s_axi_awvalid (axi_aw_valid), 85 | .s_axi_awaddr (axi_aw.addr), 86 | .s_axi_awburst (axi_aw.burst), 87 | .s_axi_awcache (axi_aw.cache), 88 | .s_axi_awid (axi_aw.id), 89 | .s_axi_awlen (axi_aw.len), 90 | .s_axi_awlock (axi_aw.lock), 91 | .s_axi_awprot (axi_aw.prot), 92 | .s_axi_awqos (axi_aw.qos), 93 | .s_axi_awsize (axi_aw.size), 94 | .s_axi_bready (axi_b_ready), 95 | .s_axi_bvalid (axi_b_valid), 96 | .s_axi_bid (axi_b.id), 97 | .s_axi_bresp (axi_b.resp), 98 | .s_axi_rready (axi_r_ready), 99 | .s_axi_rvalid (axi_r_valid), 100 | .s_axi_rdata (axi_r.data), 101 | .s_axi_rid (axi_r.id), 102 | .s_axi_rlast (axi_r.last), 103 | .s_axi_rresp (axi_r.resp), 104 | .s_axi_wready (axi_w_ready), 105 | .s_axi_wvalid (axi_w_valid), 106 | .s_axi_wdata (axi_w.data), 107 | .s_axi_wlast (axi_w.last), 108 | .s_axi_wstrb (axi_w.strb), 109 | // Other signals 110 | .device_temp ( ), 111 | .app_sr_req ('0), 112 | .app_sr_active ( ), 113 | .app_ref_req ('0), 114 | .app_ref_ack ( ), 115 | .app_zq_req ('0), 116 | .app_zq_ack ( ), 117 | .init_calib_complete ( ) 118 | ); 119 | 120 | // TileLink to AXI bridge 121 | 122 | `TL_DECLARE(DataWidth, AddrWidth, SourceWidth, 1, link_sync); 123 | 124 | tl_axi_adapter #( 125 | .DataWidth (DataWidth), 126 | .AddrWidth (AddrWidth), 127 | .SourceWidth (SourceWidth), 128 | .IdWidth (SourceWidth) 129 | ) adapter ( 130 | .clk_i (mig_clk), 131 | .rst_ni (rst_no), 132 | `TL_CONNECT_DEVICE_PORT(host, link_sync), 133 | `AXI_CONNECT_HOST_PORT(device, axi) 134 | ); 135 | 136 | // Clock converter 137 | 138 | tl_fifo_async #( 139 | .DataWidth (DataWidth), 140 | .AddrWidth (AddrWidth), 141 | .SourceWidth (SourceWidth), 142 | .SinkWidth (1), 143 | .RequestFifoDepth (32), 144 | .GrantFifoDepth (32) 145 | ) cdc ( 146 | .clk_host_i (clk_o), 147 | .rst_host_ni (rst_no), 148 | `TL_FORWARD_DEVICE_PORT(host, link), 149 | .clk_device_i (mig_clk), 150 | .rst_device_ni (rst_no), 151 | `TL_CONNECT_HOST_PORT(device, link_sync) 152 | ); 153 | 154 | endmodule 155 | -------------------------------------------------------------------------------- /docs/tutorial.md: -------------------------------------------------------------------------------- 1 | # Tutorial 2 | 3 | ## Hardware Supported 4 | 5 | To run this tutorial, you will need one of the supported FPGA boards. Currently, the following FPGAs 6 | are supported: 7 | * Nexys A7 8 | * Nexys Video 9 | * Genesys 2 10 | 11 | For this tutotial you will also need a microSD card (and a card reader). 12 | 13 | ## Environment Setup 14 | 15 | Ensure Python 3, Make and GCC toolchains are installed. They should be available from your distribution. If you are using a Debian-based system, 16 | you can install them with the following command: 17 | ```bash 18 | sudo apt-get install python3 build-essential 19 | ``` 20 | 21 | Hardware synthesis and implementation needs the following extra tools: 22 | * Vivado 23 | * [FuseSoC](https://github.com/olofk/fusesoc) 24 | * [Edalize](https://github.com/olofk/edalize) 25 | * [Mako](https://github.com/sqlalchemy/mako) 26 | 27 | Vivado needs to be obtained from [Xilinx](https://www.xilinx.com/support/download.html). A license may be needed depending on your FPGA. Please refer to Xilinx documentation for more information. Please ensure that `vivado` is in your PATH. 28 | Please use a Vivado version that is at least v2020.2. 29 | 30 | FuseSoc, Edalize and Mako can be installed with PIP. Change the working directory to the root of the project, and run: 31 | ```bash 32 | pip3 install -r python-requirements.txt 33 | ``` 34 | 35 | Firmware and software compilatin needs the following tools: 36 | * Device tree compiler 37 | * Flex and Bison 38 | * GCC toolchain for cross-compilation 39 | * Rustc and Cargo 40 | 41 | Device tree compiler, flex and bison should be available from your distribution. If you are using a Debian-based system, 42 | you can install them with the following command: 43 | ```bash 44 | sudo apt-get install device-tree-compiler flex bison 45 | ``` 46 | 47 | If you are using a Debian-based system, RISC-V cross compilers can also be fetched using apt-get: 48 | ```bash 49 | sudo apt-get install gcc-riscv64-linux-gnu 50 | ``` 51 | If you want to build the cross compiler yourself, you can follow [this guide](https://wiki.osdev.org/GCC_Cross-Compiler) 52 | and compile for target riscv64-unknown-linux-gnu. 53 | 54 | The Rust toolchain can obtained from [Rustup](https://rustup.rs/). 55 | ```bash 56 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 57 | ``` 58 | Nightly compiler is required, and riscv64imac-unknown-none-elf target is needed. Though this should 59 | normally be automatically installed by Rustup when compiling the firmware for the first time since 60 | we have provided `rust-toolchain.toml` file. 61 | 62 | ## Building Bitstream and Firmware 63 | 64 | Before building, first ensure that all submodules are up to date: 65 | ```bash 66 | git submodule update --init --recursive 67 | ``` 68 | 69 | Then cd into the directory that corresponds to your FPGA board, e.g. `cd nexys_a7`. 70 | 71 | The bitstream can be built using `make bitstream`. The firmware can be built using `make firmware`. 72 | 73 | ## Building Kernel and Distro Image 74 | 75 | Kernel and distro images are agnostic to different FPGA boards. 76 | 77 | In the root directory of the project, the kernel can be built using `make vmlinux`. 78 | 79 | Debian image can be built using `make rootfs.img`. It should be noted that bootstrapping Debian image requires root privileges and a Debian-based system. 80 | 81 | ## Preparing the SD Card 82 | 83 | Connect the SD card reader to your machien and insert the microSD card. Assuming the card is located at `/dev/mmcblk0`, then you can write the Debian image to the microSD card: 84 | ```bash 85 | sudo dd if=rootfs.img of=/dev/mmcblk0 bs=1M 86 | ``` 87 | Note that this will overwrite the contents on the microSD card. 88 | 89 | Then you can expand the file system to the full size of the card: 90 | ```bash 91 | sudo resize2fs /dev/mmcblk0 92 | ``` 93 | 94 | Next mount the file system and copy the kernel into the file system: 95 | ```bash 96 | sudo mount /dev/mmcblk0 /mnt 97 | sudo cp vmlinux /mnt 98 | sudo umount /mnt 99 | ``` 100 | 101 | The card should be ready. You can now eject the card and insert it onto the FPGA board. 102 | 103 | ## Programming the FPGA 104 | 105 | Now here is the final step. The firmware needs to be programmed onto the on-board flash memory. 106 | 107 | Ensure that the board is connected to your machine, drivers are installed and the Xilinx HW server is running, and run 108 | ```bash 109 | make program-flash-firmware 110 | ``` 111 | in the board-specific directory. 112 | 113 | Open your TTY program of choice on the FPGA-bound link with baud 230400, and run 114 | ```bash 115 | make program-bitstream 116 | ``` 117 | to load the bitstream onto the FPGA. 118 | 119 | If everything goes well you should start to see output from the firmware and Linux booting! 120 | 121 | If you want to persist the bitstream, you can program it along with the firmware to the flash with 122 | ```bash 123 | make program-flash 124 | ``` 125 | You may need to switch the jumper on the board to switch the source from QSPI. -------------------------------------------------------------------------------- /genesys_2/rtl/ddr.sv: -------------------------------------------------------------------------------- 1 | `include "axi_util.svh" 2 | `include "tl_util.svh" 3 | 4 | module ddr #( 5 | parameter DataWidth = 128, 6 | parameter AddrWidth = 30, 7 | parameter SourceWidth = 5 8 | ) ( 9 | // Clock and reset 10 | input sys_clk_p, 11 | input sys_clk_n, 12 | input sys_rst_ni, 13 | 14 | output clk_o, 15 | output io_clk_o, 16 | output rst_no, 17 | 18 | // IO port 19 | output [14:0] ddr3_sdram_addr, 20 | output [2:0] ddr3_sdram_ba, 21 | output ddr3_sdram_cas_n, 22 | output [0:0] ddr3_sdram_ck_n, 23 | output [0:0] ddr3_sdram_ck_p, 24 | output [0:0] ddr3_sdram_cke, 25 | output [0:0] ddr3_sdram_cs_n, 26 | output [3:0] ddr3_sdram_dm, 27 | inout [31:0] ddr3_sdram_dq, 28 | inout [3:0] ddr3_sdram_dqs_n, 29 | inout [3:0] ddr3_sdram_dqs_p, 30 | output [0:0] ddr3_sdram_odt, 31 | output ddr3_sdram_ras_n, 32 | output ddr3_sdram_reset_n, 33 | output ddr3_sdram_we_n, 34 | 35 | // TileLink port 36 | `TL_DECLARE_DEVICE_PORT(DataWidth, AddrWidth, SourceWidth, 1, link) 37 | ); 38 | 39 | `AXI_DECLARE(DataWidth, AddrWidth, SourceWidth, axi); 40 | 41 | wire mig_clk; 42 | 43 | mig_7series_0 ddr_ctrl ( 44 | // Clock 45 | .sys_clk_p (sys_clk_p), 46 | .sys_clk_n (sys_clk_n), 47 | .sys_rst (sys_rst_ni), 48 | .ui_clk (mig_clk), 49 | .ui_clk_sync_rst (), 50 | .ui_addn_clk_0 (clk_o), 51 | .ui_addn_clk_1 (io_clk_o), 52 | .ui_addn_clk_2 (), 53 | .ui_addn_clk_3 (), 54 | .ui_addn_clk_4 (), 55 | .mmcm_locked (rst_no), 56 | // DDR interface 57 | .ddr3_addr (ddr3_sdram_addr), 58 | .ddr3_ba (ddr3_sdram_ba), 59 | .ddr3_cas_n (ddr3_sdram_cas_n), 60 | .ddr3_ck_n (ddr3_sdram_ck_n), 61 | .ddr3_ck_p (ddr3_sdram_ck_p), 62 | .ddr3_cke (ddr3_sdram_cke), 63 | .ddr3_cs_n (ddr3_sdram_cs_n), 64 | .ddr3_dm (ddr3_sdram_dm), 65 | .ddr3_dq (ddr3_sdram_dq), 66 | .ddr3_dqs_n (ddr3_sdram_dqs_n), 67 | .ddr3_dqs_p (ddr3_sdram_dqs_p), 68 | .ddr3_odt (ddr3_sdram_odt), 69 | .ddr3_ras_n (ddr3_sdram_ras_n), 70 | .ddr3_reset_n (ddr3_sdram_reset_n), 71 | .ddr3_we_n (ddr3_sdram_we_n), 72 | // AXI interface 73 | .aresetn (rst_no), 74 | .s_axi_arready (axi_ar_ready), 75 | .s_axi_arvalid (axi_ar_valid), 76 | .s_axi_araddr (axi_ar.addr), 77 | .s_axi_arburst (axi_ar.burst), 78 | .s_axi_arcache (axi_ar.cache), 79 | .s_axi_arid (axi_ar.id), 80 | .s_axi_arlen (axi_ar.len), 81 | .s_axi_arlock (axi_ar.lock), 82 | .s_axi_arprot (axi_ar.prot), 83 | .s_axi_arqos (axi_ar.qos), 84 | .s_axi_arsize (axi_ar.size), 85 | .s_axi_awready (axi_aw_ready), 86 | .s_axi_awvalid (axi_aw_valid), 87 | .s_axi_awaddr (axi_aw.addr), 88 | .s_axi_awburst (axi_aw.burst), 89 | .s_axi_awcache (axi_aw.cache), 90 | .s_axi_awid (axi_aw.id), 91 | .s_axi_awlen (axi_aw.len), 92 | .s_axi_awlock (axi_aw.lock), 93 | .s_axi_awprot (axi_aw.prot), 94 | .s_axi_awqos (axi_aw.qos), 95 | .s_axi_awsize (axi_aw.size), 96 | .s_axi_bready (axi_b_ready), 97 | .s_axi_bvalid (axi_b_valid), 98 | .s_axi_bid (axi_b.id), 99 | .s_axi_bresp (axi_b.resp), 100 | .s_axi_rready (axi_r_ready), 101 | .s_axi_rvalid (axi_r_valid), 102 | .s_axi_rdata (axi_r.data), 103 | .s_axi_rid (axi_r.id), 104 | .s_axi_rlast (axi_r.last), 105 | .s_axi_rresp (axi_r.resp), 106 | .s_axi_wready (axi_w_ready), 107 | .s_axi_wvalid (axi_w_valid), 108 | .s_axi_wdata (axi_w.data), 109 | .s_axi_wlast (axi_w.last), 110 | .s_axi_wstrb (axi_w.strb), 111 | // Other signals 112 | .device_temp_i ('0), 113 | .device_temp ( ), 114 | .app_sr_req ('0), 115 | .app_sr_active ( ), 116 | .app_ref_req ('0), 117 | .app_ref_ack ( ), 118 | .app_zq_req ('0), 119 | .app_zq_ack ( ), 120 | .init_calib_complete ( ) 121 | ); 122 | 123 | // TileLink to AXI bridge 124 | 125 | `TL_DECLARE(DataWidth, AddrWidth, SourceWidth, 1, link_sync); 126 | 127 | tl_axi_adapter #( 128 | .DataWidth (DataWidth), 129 | .AddrWidth (AddrWidth), 130 | .SourceWidth (SourceWidth), 131 | .IdWidth (SourceWidth) 132 | ) adapter ( 133 | .clk_i (mig_clk), 134 | .rst_ni (rst_no), 135 | `TL_CONNECT_DEVICE_PORT(host, link_sync), 136 | `AXI_CONNECT_HOST_PORT(device, axi) 137 | ); 138 | 139 | // Clock converter 140 | 141 | tl_fifo_async #( 142 | .DataWidth (DataWidth), 143 | .AddrWidth (AddrWidth), 144 | .SourceWidth (SourceWidth), 145 | .SinkWidth (1), 146 | .RequestFifoDepth (32), 147 | .GrantFifoDepth (32) 148 | ) cdc ( 149 | .clk_host_i (clk_o), 150 | .rst_host_ni (rst_no), 151 | `TL_FORWARD_DEVICE_PORT(host, link), 152 | .clk_device_i (mig_clk), 153 | .rst_device_ni (rst_no), 154 | `TL_CONNECT_HOST_PORT(device, link_sync) 155 | ); 156 | 157 | endmodule 158 | -------------------------------------------------------------------------------- /chip_top.core: -------------------------------------------------------------------------------- 1 | CAPI=2: 2 | # SPDX-License-Identifier: MIT OR Apache-2.0 3 | name: "garyguo.net:systems:muntjac_soc:0.1" 4 | description: "Muntjac SoC toplevel" 5 | filesets: 6 | files_rtl: 7 | depend: 8 | - garyguo.net:systems:muntjac_core_wrapper 9 | - lowrisc:tl:fifo_async 10 | - lowrisc:tl:axi_adapter 11 | - lowrisc:tl:axi_lite_adapter 12 | - lowrisc:axi:common 13 | - lowrisc:axi_lite:common 14 | - lowrisc:tl:adapter_bram 15 | - garyguo.net:peripheral:sdhci 16 | - lowrisc:tl:socket_m1 17 | - lowrisc:tl:socket_1n 18 | - lowrisc:muntjac:llc 19 | - lowrisc:tl:adapter 20 | - lowrisc:tl:ram_terminator 21 | - lowrisc:tl:io_terminator 22 | - lowrisc:tl:error_sink 23 | - lowrisc:axi:tl_adapter 24 | files: 25 | - rtl/spi.sv 26 | - rtl/uart.sv 27 | - rtl/sdhci.sv 28 | - rtl/ps2.sv 29 | - rtl/ccx.sv 30 | - rtl/uncore/clint.sv 31 | - rtl/uncore/clint_tl.sv 32 | - rtl/uncore/plic.sv 33 | - rtl/uncore/plic_tl.sv 34 | file_type: systemVerilogSource 35 | 36 | files_rtl_nexys_a7: 37 | files: 38 | - nexys_a7/rtl/chip_top.sv 39 | - nexys_a7/rtl/ddr.sv 40 | - nexys_a7/rtl/eth.sv 41 | file_type: systemVerilogSource 42 | 43 | files_rtl_nexys_video: 44 | depend: 45 | - garyguo.net:peripheral:display_controller 46 | files: 47 | - nexys_video/rtl/chip_top.sv 48 | - nexys_video/rtl/ddr.sv 49 | - nexys_video/rtl/dvi.sv 50 | file_type: systemVerilogSource 51 | 52 | files_rtl_genesys_2: 53 | depend: 54 | - garyguo.net:peripheral:display_controller 55 | files: 56 | - genesys_2/rtl/chip_top.sv 57 | - genesys_2/rtl/ddr.sv 58 | - genesys_2/rtl/eth.sv 59 | - genesys_2/rtl/dvi.sv 60 | file_type: systemVerilogSource 61 | 62 | files_constraints_nexys_a7: 63 | files: 64 | - nexys_a7/data/clocks.xdc 65 | - nexys_a7/data/pins.xdc 66 | file_type: xdc 67 | 68 | files_constraints_nexys_video: 69 | files: 70 | - nexys_video/data/clocks.xdc 71 | - nexys_video/data/pins.xdc 72 | file_type: xdc 73 | 74 | files_constraints_genesys_2: 75 | files: 76 | - genesys_2/data/clocks.xdc 77 | - genesys_2/data/pins.xdc 78 | file_type: xdc 79 | 80 | files_tcl_nexys_a7: 81 | files: 82 | - nexys_a7/util/vivado_create_ip.tcl 83 | - nexys_a7/util/create_eth_ip.tcl 84 | # File copied by fusesoc into the workroot 85 | - nexys_a7/data/mig.prj: { file_type: user, copyto: mig.prj } 86 | - data/mii_to_rmii_v2_0.tar : { file_type: user, copyto: mii_to_rmii_v2_0.tar } 87 | file_type: tclSource 88 | 89 | files_tcl_nexys_video: 90 | files: 91 | - nexys_video/util/vivado_create_ip.tcl 92 | - nexys_video/util/create_eth_ip.tcl 93 | # Files copied by fusesoc into the workroot 94 | - nexys_video/data/mig.prj : { file_type: user, copyto: mig.prj } 95 | - data/rgb2dvi.tar : { file_type: user, copyto: rgb2dvi.tar } 96 | - data/axi_ps2_1.0.tar.gz : { file_type: user, copyto: axi_ps2_1.0.tar.gz } 97 | file_type: tclSource 98 | 99 | files_tcl_genesys_2: 100 | files: 101 | - genesys_2/util/vivado_create_ip.tcl 102 | - genesys_2/util/create_eth_ip.tcl 103 | # File copied by fusesoc into the workroot 104 | - genesys_2/data/mig.prj: { file_type: user, copyto: mig.prj } 105 | - data/rgb2dvi.tar : { file_type: user, copyto: rgb2dvi.tar } 106 | - data/axi_ps2_1.0.tar.gz : { file_type: user, copyto: axi_ps2_1.0.tar.gz } 107 | file_type: tclSource 108 | 109 | parameters: 110 | PRIM_DEFAULT_IMPL: 111 | datatype: str 112 | paramtype: vlogdefine 113 | description: Primitives implementation to use, e.g. "prim_pkg::ImplGeneric". 114 | 115 | targets: 116 | default: &default_target 117 | filesets: 118 | - files_rtl 119 | toplevel: chip_top 120 | 121 | nexys_a7: 122 | default_tool: vivado 123 | filesets: 124 | - files_rtl 125 | - files_rtl_nexys_a7 126 | - files_constraints_nexys_a7 127 | - files_tcl_nexys_a7 128 | toplevel: chip_top 129 | parameters: 130 | - PRIM_DEFAULT_IMPL=prim_pkg::ImplXilinx 131 | tools: 132 | vivado: 133 | part: "xc7a100tcsg324-1" 134 | 135 | nexys_video: 136 | default_tool: vivado 137 | filesets: 138 | - files_rtl 139 | - files_rtl_nexys_video 140 | - files_constraints_nexys_video 141 | - files_tcl_nexys_video 142 | toplevel: chip_top 143 | parameters: 144 | - PRIM_DEFAULT_IMPL=prim_pkg::ImplXilinx 145 | tools: 146 | vivado: 147 | part: "xc7a200tsbg484-1" 148 | 149 | genesys_2: 150 | default_tool: vivado 151 | filesets: 152 | - files_rtl 153 | - files_rtl_genesys_2 154 | - files_constraints_genesys_2 155 | - files_tcl_genesys_2 156 | toplevel: chip_top 157 | parameters: 158 | - PRIM_DEFAULT_IMPL=prim_pkg::ImplXilinx 159 | tools: 160 | vivado: 161 | part: "xc7k325tffg900-2" 162 | -------------------------------------------------------------------------------- /firmware/src/allocator.rs: -------------------------------------------------------------------------------- 1 | //! Scoped allocator. 2 | //! 3 | //! Scoped allocator has nested scopes. Within each scope, it allocates in a linear incremental 4 | //! fashion and never performs any deallocation. It will however track whether the number of items 5 | //! yet to deallocate. When scope ends, it will check if everything allocated within the scope is 6 | //! deallocated, and panic if not. 7 | 8 | use buddy::BuddyAllocator; 9 | use core::alloc::{GlobalAlloc, Layout}; 10 | use ro_cell::RoCell; 11 | use spin::Mutex; 12 | 13 | // SAFETY: This is initialized in `init`. 14 | static FIRMWARE_ALLOC: RoCell = unsafe { RoCell::new_uninit() }; 15 | 16 | struct MemoryBlock { 17 | alloc: Option>, 18 | start: usize, 19 | end: usize, 20 | num: usize, 21 | parent: Option<&'static mut MemoryBlock>, 22 | } 23 | 24 | struct ScopedAllocator(Mutex>); 25 | 26 | unsafe impl GlobalAlloc for ScopedAllocator { 27 | unsafe fn alloc(&self, layout: Layout) -> *mut u8 { 28 | let mut guard = self.0.lock(); 29 | match *guard { 30 | None => FIRMWARE_ALLOC.alloc(layout), 31 | Some(ref mut block) => { 32 | let ptr = block.alloc.as_ref().unwrap().alloc(layout); 33 | if !ptr.is_null() { 34 | block.num += 1; 35 | } 36 | ptr 37 | } 38 | } 39 | } 40 | unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { 41 | let ptr = ptr as usize; 42 | 43 | let mut guard = self.0.lock(); 44 | let mut block = guard.as_deref_mut(); 45 | while let Some(cur_block) = block { 46 | if ptr >= cur_block.start && ptr < cur_block.end { 47 | cur_block.num -= 1; 48 | cur_block.alloc.as_ref().unwrap().dealloc(ptr as _, layout); 49 | return; 50 | } 51 | block = cur_block.parent.as_deref_mut(); 52 | } 53 | 54 | FIRMWARE_ALLOC.dealloc(ptr as _, layout); 55 | } 56 | 57 | unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { 58 | // Reallocation must happen with the original allocator. 59 | let ptr = ptr as usize; 60 | 61 | let mut guard = self.0.lock(); 62 | let mut block = guard.as_deref_mut(); 63 | while let Some(cur_block) = block { 64 | if ptr >= cur_block.start && ptr < cur_block.end { 65 | return cur_block 66 | .alloc 67 | .as_ref() 68 | .unwrap() 69 | .realloc(ptr as _, layout, new_size); 70 | } 71 | block = cur_block.parent.as_deref_mut(); 72 | } 73 | 74 | FIRMWARE_ALLOC.realloc(ptr as _, layout, new_size) 75 | } 76 | } 77 | 78 | #[global_allocator] 79 | static A: ScopedAllocator = ScopedAllocator(Mutex::new(None)); 80 | 81 | #[allow(dead_code)] 82 | pub fn scoped_with_memory R>(mem: &mut [u8], f: F) -> R { 83 | let buddy = BuddyAllocator::new(unsafe { core::mem::transmute(&mut *mem) }).unwrap(); 84 | let mut guard = A.0.lock(); 85 | let mut memory_block = MemoryBlock { 86 | alloc: Some(buddy), 87 | start: mem.as_ptr() as usize, 88 | end: mem.as_ptr() as usize + mem.len(), 89 | num: 0, 90 | parent: guard.take(), 91 | }; 92 | 93 | *guard = Some(unsafe { &mut *(&mut memory_block as *mut MemoryBlock) }); 94 | drop(guard); 95 | 96 | let ret = f(); 97 | 98 | guard = A.0.lock(); 99 | *guard = memory_block.parent; 100 | 101 | assert!(memory_block.num == 0, "some allocations are leaked"); 102 | ret 103 | } 104 | 105 | #[allow(dead_code)] 106 | pub fn scoped R>(f: F) -> R { 107 | let mut guard = A.0.lock(); 108 | let parent = guard.take().expect("no allocation scope active"); 109 | let mut memory_block = MemoryBlock { 110 | alloc: parent.alloc.take(), 111 | start: parent.start, 112 | end: parent.end, 113 | num: 0, 114 | parent: Some(parent), 115 | }; 116 | 117 | *guard = Some(unsafe { &mut *(&mut memory_block as *mut MemoryBlock) }); 118 | drop(guard); 119 | 120 | let ret = f(); 121 | 122 | guard = A.0.lock(); 123 | let parent = memory_block.parent.unwrap(); 124 | parent.alloc = memory_block.alloc; 125 | *guard = Some(parent); 126 | 127 | assert!(memory_block.num == 0, "some allocations are leaked"); 128 | ret 129 | } 130 | 131 | pub fn init() { 132 | extern "C" { 133 | static _end: u8; 134 | } 135 | // Account for the stack size 136 | let firmware_memory_end = crate::address::MEMORY_BASE + crate::address::MEMORY_SIZE 137 | - (1 << 14) * crate::ipi::hart_count(); 138 | let firmware_memory_start = unsafe { &_end as *const u8 as usize }; 139 | let free_memory = unsafe { 140 | core::slice::from_raw_parts_mut( 141 | firmware_memory_start as _, 142 | firmware_memory_end - firmware_memory_start, 143 | ) 144 | }; 145 | 146 | let alloc = buddy::BuddyAllocator::new(free_memory).unwrap(); 147 | unsafe { RoCell::init(&FIRMWARE_ALLOC, alloc) }; 148 | } 149 | -------------------------------------------------------------------------------- /rtl/uncore/plic.sv: -------------------------------------------------------------------------------- 1 | module plic #( 2 | parameter NUM_CONTEXTS = 1, 3 | // Currently can only be 32. 4 | parameter NUM_IRQS = 32 5 | ) ( 6 | input logic clk, 7 | input logic rstn, 8 | 9 | // Interrupt sources. Bit 0 is ignored. 10 | input logic [NUM_IRQS-1:0] interrupts, 11 | 12 | // Whether each interrupt should be level triggered or edge triggered 13 | input logic [NUM_IRQS-1:0] edge_trigger, 14 | 15 | // We expose the control as a 4MiB BRAM. 16 | input logic [21:0] bram_addr, 17 | input logic bram_en, 18 | input logic bram_we, 19 | output logic [31:0] bram_rddata, 20 | input logic [31:0] bram_wrdata, 21 | 22 | // IRQ output for each context. 23 | output logic [NUM_CONTEXTS-1:0] irq 24 | ); 25 | 26 | if (NUM_IRQS > 32) begin 27 | $error("At most 32 IRQs are supported at the moment"); 28 | end 29 | 30 | if (NUM_CONTEXTS > 64) begin 31 | $error("At most 64 contexts are supported at the moment"); 32 | end 33 | 34 | localparam ADDR_WIDTH = 22; 35 | 36 | logic [NUM_IRQS-1:0] prev_interrupts; 37 | 38 | logic [NUM_IRQS-1:0] pending; 39 | logic [NUM_IRQS-1:0] claimed; 40 | logic [NUM_CONTEXTS-1:0][NUM_IRQS-1:0] enable; 41 | 42 | for (genvar ctx_id = 0; ctx_id < NUM_CONTEXTS; ctx_id++) begin 43 | assign irq[ctx_id] = |(pending & ~claimed & enable[ctx_id]); 44 | end 45 | 46 | // 47 | // Matching addresses 48 | // 49 | 50 | wire priority_match = bram_addr[ADDR_WIDTH-1:12] == 0; 51 | 52 | wire pending_match = bram_addr[ADDR_WIDTH-1:7] == 'b1_00000; 53 | wire [4:0] pending_irq = bram_addr[6:2]; 54 | 55 | // This currently matches 0x2000 to 0x4000, i.e. 64 contexts 56 | wire enable_match = bram_addr[ADDR_WIDTH-1:13] == 1; 57 | wire [5:0] enable_ctx = bram_addr[12:7]; 58 | wire [4:0] enable_irq = bram_addr[6:2]; 59 | 60 | wire threshold_match = bram_addr[ADDR_WIDTH-1:18] == 'b1_000 && bram_addr[11:2] == 0; 61 | wire claim_match = bram_addr[ADDR_WIDTH-1:18] == 'b1_000 && bram_addr[11:2] == 1; 62 | wire [5:0] match_ctx = bram_addr[17:12]; 63 | 64 | // 65 | // BRAM Interfacing logic 66 | // 67 | 68 | always_ff @(posedge clk or negedge rstn) 69 | if (!rstn) begin 70 | prev_interrupts <= '1; 71 | pending <= '0; 72 | claimed <= '0; 73 | enable <= '0; 74 | bram_rddata <= 'x; 75 | end 76 | else begin 77 | prev_interrupts <= interrupts; 78 | // Set pending when we receive the interrupt. 79 | pending <= pending | ((~edge_trigger | ~prev_interrupts) & interrupts); 80 | 81 | if (bram_en) begin 82 | bram_rddata <= 'x; 83 | unique case (1'b1) 84 | // Priority; we hardwire them to 1 85 | priority_match: bram_rddata <= 1; 86 | // Pending, IRQ 0-31 87 | pending_match: bram_rddata <= pending_irq == 0 ? pending : 0; 88 | // Enable, CTX 0, IRQ 0-31 89 | enable_match: begin 90 | if (enable_irq == 0 && enable_ctx < NUM_CONTEXTS) begin 91 | bram_rddata <= enable[enable_ctx]; 92 | if (bram_we) begin 93 | enable[enable_ctx] <= bram_wrdata; 94 | end 95 | end 96 | else begin 97 | bram_rddata <= 0; 98 | end 99 | end 100 | // Threshold, CTX 0; we hardwire them to 0 101 | threshold_match: bram_rddata <= 0; 102 | // Claim, CTX 0 103 | claim_match: begin 104 | if (match_ctx < NUM_CONTEXTS) begin 105 | if (bram_we) begin 106 | claimed[bram_wrdata[4:0]] <= 1'b0; 107 | bram_rddata <= 'x; 108 | end 109 | else begin 110 | automatic logic [NUM_IRQS-1:0] claimable = pending & ~claimed & enable[match_ctx]; 111 | automatic logic [4:0] irq = 0; 112 | for (int i = NUM_IRQS - 1; i > 0; i--) begin 113 | if (claimable[i]) begin 114 | irq = i; 115 | end 116 | end 117 | pending[irq] <= 1'b0; 118 | claimed[irq] <= 1'b1; 119 | bram_rddata <= irq; 120 | end 121 | end 122 | else begin 123 | bram_rddata <= 0; 124 | end 125 | end 126 | // Out of bound access 127 | default: begin 128 | bram_rddata <= 0; 129 | $warning("PLIC out-of-bound access at %h", bram_addr); 130 | end 131 | endcase 132 | end 133 | end 134 | 135 | endmodule 136 | -------------------------------------------------------------------------------- /nexys_a7/util/vivado_create_ip.tcl: -------------------------------------------------------------------------------- 1 | set workroot [pwd] 2 | 3 | # Ensure that mii_to_rmii_v2_0 IP is extracted 4 | if {!([file exists "${workroot}/ip/mii_to_rmii_v2_0"])} { 5 | exec mkdir -p "${workroot}/ip" 6 | exec tar -xvzf "${workroot}/mii_to_rmii_v2_0.tar" -C "${workroot}/ip" 7 | } 8 | set_property ip_repo_paths [file normalize "${workroot}/ip"] [current_project] 9 | update_ip_catalog 10 | 11 | set_property used_in_synthesis false [get_files clocks.xdc] 12 | 13 | # Enable Retiming in synthesis 14 | set_property STEPS.SYNTH_DESIGN.ARGS.RETIMING true [get_runs synth_1] 15 | 16 | # Create DDR Memory Controller 17 | # This IP needs a 200MHz+ clock so we will need a clock wizard. 18 | create_ip -name mig_7series -vendor xilinx.com -library ip -module_name mig_7series_0 19 | set_property CONFIG.XML_INPUT_FILE [file normalize "${workroot}/mig.prj"] [get_ips mig_7series_0] 20 | 21 | create_ip -name clk_wiz -vendor xilinx.com -library ip -version 6.0 -module_name clk_wiz_ddr 22 | set_property -dict [list \ 23 | CONFIG.CLKOUT2_USED {true} \ 24 | CONFIG.CLKOUT3_USED {true} \ 25 | CONFIG.CLKOUT1_REQUESTED_OUT_FREQ {200.000} \ 26 | CONFIG.CLKOUT2_REQUESTED_OUT_FREQ {40} \ 27 | CONFIG.CLKOUT3_REQUESTED_OUT_FREQ {50} \ 28 | CONFIG.RESET_TYPE {ACTIVE_LOW} \ 29 | CONFIG.CLKOUT1_DRIVES {BUFG} \ 30 | CONFIG.CLKOUT2_DRIVES {BUFG} \ 31 | CONFIG.CLKOUT3_DRIVES {BUFG} \ 32 | CONFIG.CLKOUT4_DRIVES {BUFG} \ 33 | CONFIG.CLKOUT5_DRIVES {BUFG} \ 34 | CONFIG.CLKOUT6_DRIVES {BUFG} \ 35 | CONFIG.CLKOUT7_DRIVES {BUFG} \ 36 | CONFIG.FEEDBACK_SOURCE {FDBK_AUTO} \ 37 | CONFIG.MMCM_BANDWIDTH {OPTIMIZED} \ 38 | CONFIG.MMCM_CLKFBOUT_MULT_F {10} \ 39 | CONFIG.MMCM_COMPENSATION {ZHOLD} \ 40 | CONFIG.MMCM_CLKOUT0_DIVIDE_F {5} \ 41 | CONFIG.MMCM_CLKOUT1_DIVIDE {25} \ 42 | CONFIG.MMCM_CLKOUT2_DIVIDE {20} \ 43 | CONFIG.NUM_OUT_CLKS {3} \ 44 | CONFIG.RESET_PORT {resetn} \ 45 | CONFIG.CLKOUT1_JITTER {114.829} \ 46 | CONFIG.CLKOUT1_PHASE_ERROR {98.575} \ 47 | CONFIG.CLKOUT2_JITTER {159.371} \ 48 | CONFIG.CLKOUT2_PHASE_ERROR {98.575} \ 49 | CONFIG.CLKOUT3_JITTER {151.636} \ 50 | CONFIG.CLKOUT3_PHASE_ERROR {98.575} 51 | ] [get_ips clk_wiz_ddr] 52 | 53 | # Create AXI Clock Converter for DDR 54 | create_ip -name axi_clock_converter -vendor xilinx.com -library ip -version 2.1 -module_name axi_clock_converter_ddr 55 | set_property -dict [list \ 56 | CONFIG.ADDR_WIDTH {27} \ 57 | CONFIG.DATA_WIDTH {128} \ 58 | CONFIG.ID_WIDTH {8} 59 | ] [get_ips axi_clock_converter_ddr] 60 | 61 | # Create AXI Quad SPI 62 | create_ip -name axi_quad_spi -vendor xilinx.com -library ip -version 3.2 -module_name axi_quad_spi_0 63 | set_property -dict [list \ 64 | CONFIG.C_SPI_MEMORY {3} \ 65 | CONFIG.C_USE_STARTUP {0} \ 66 | CONFIG.C_USE_STARTUP_INT {0} \ 67 | CONFIG.C_SPI_MODE {1} \ 68 | CONFIG.C_SCK_RATIO {2} \ 69 | CONFIG.C_XIP_MODE {1} \ 70 | CONFIG.C_XIP_PERF_MODE {0} \ 71 | CONFIG.C_TYPE_OF_AXI4_INTERFACE {1} \ 72 | CONFIG.C_S_AXI4_ID_WIDTH {1} \ 73 | CONFIG.Async_Clk {1} \ 74 | ] [get_ips axi_quad_spi_0] 75 | 76 | # Create AXI UART16550 77 | # This IP has a requirement for its input clock so we use io_clk instead of bus clk 78 | # and need an extra clock converter. 79 | # We also need a clock wizard for a 18.432MHz clock which works better for most baud rates. 80 | create_ip -name axi_uart16550 -vendor xilinx.com -library ip -version 2.0 -module_name axi_uart16550_0 81 | set_property -dict [list \ 82 | CONFIG.C_S_AXI_ACLK_FREQ_HZ_d {50} \ 83 | CONFIG.C_HAS_EXTERNAL_XIN {1} \ 84 | CONFIG.C_EXTERNAL_XIN_CLK_HZ_d {18.432} \ 85 | CONFIG.C_S_AXI_ACLK_FREQ_HZ {50000000} \ 86 | CONFIG.C_EXTERNAL_XIN_CLK_HZ {18432000} \ 87 | ] [get_ips axi_uart16550_0] 88 | 89 | create_ip -name axi_clock_converter -vendor xilinx.com -library ip -version 2.1 -module_name axi_clock_converter_uart 90 | set_property -dict [list \ 91 | CONFIG.PROTOCOL {AXI4LITE} \ 92 | CONFIG.ADDR_WIDTH {13} \ 93 | CONFIG.DATA_WIDTH {32} \ 94 | CONFIG.ID_WIDTH {0} \ 95 | CONFIG.AWUSER_WIDTH {0} \ 96 | CONFIG.ARUSER_WIDTH {0} \ 97 | CONFIG.RUSER_WIDTH {0} \ 98 | CONFIG.WUSER_WIDTH {0} \ 99 | CONFIG.BUSER_WIDTH {0} \ 100 | ] [get_ips axi_clock_converter_uart] 101 | 102 | create_ip -name clk_wiz -vendor xilinx.com -library ip -version 6.0 -module_name clk_wiz_uart 103 | set_property -dict [list \ 104 | CONFIG.PRIMITIVE {PLL} \ 105 | CONFIG.PRIM_SOURCE {No_buffer} \ 106 | CONFIG.PRIM_IN_FREQ {50} \ 107 | CONFIG.CLKOUT1_REQUESTED_OUT_FREQ {18.432} \ 108 | CONFIG.RESET_TYPE {ACTIVE_LOW} \ 109 | CONFIG.CLKIN1_JITTER_PS {200.0} \ 110 | CONFIG.CLKOUT1_DRIVES {BUFG} \ 111 | CONFIG.CLKOUT2_DRIVES {BUFG} \ 112 | CONFIG.CLKOUT3_DRIVES {BUFG} \ 113 | CONFIG.CLKOUT4_DRIVES {BUFG} \ 114 | CONFIG.CLKOUT5_DRIVES {BUFG} \ 115 | CONFIG.CLKOUT6_DRIVES {BUFG} \ 116 | CONFIG.CLKOUT7_DRIVES {BUFG} \ 117 | CONFIG.MMCM_DIVCLK_DIVIDE {2} \ 118 | CONFIG.MMCM_BANDWIDTH {OPTIMIZED} \ 119 | CONFIG.MMCM_CLKFBOUT_MULT_F {59} \ 120 | CONFIG.MMCM_CLKIN1_PERIOD {20.000} \ 121 | CONFIG.MMCM_CLKIN2_PERIOD {10.0} \ 122 | CONFIG.MMCM_COMPENSATION {ZHOLD} \ 123 | CONFIG.MMCM_CLKOUT0_DIVIDE_F {80} \ 124 | CONFIG.RESET_PORT {resetn} \ 125 | CONFIG.CLKOUT1_JITTER {180.940} \ 126 | CONFIG.CLKOUT1_PHASE_ERROR {220.889} \ 127 | ] [get_ips clk_wiz_uart] 128 | -------------------------------------------------------------------------------- /genesys_2/data/pins.xdc: -------------------------------------------------------------------------------- 1 | set_property PACKAGE_PIN R19 [get_ports sys_rst_ni] 2 | set_property IOSTANDARD LVCMOS33 [get_ports sys_rst_ni] 3 | 4 | set_property PACKAGE_PIN U19 [get_ports qspi_csn] 5 | set_property IOSTANDARD LVCMOS33 [get_ports qspi_csn] 6 | set_property PACKAGE_PIN P24 [get_ports qspi_dq[0]] 7 | set_property IOSTANDARD LVCMOS33 [get_ports qspi_dq[0]] 8 | set_property PACKAGE_PIN R25 [get_ports qspi_dq[1]] 9 | set_property IOSTANDARD LVCMOS33 [get_ports qspi_dq[1]] 10 | #set_property PACKAGE_PIN R20 [get_ports qspi_dq[2]] 11 | #set_property IOSTANDARD LVCMOS33 [get_ports qspi_dq[2]] 12 | #set_property PACKAGE_PIN R21 [get_ports qspi_dq[3]] 13 | #set_property IOSTANDARD LVCMOS33 [get_ports qspi_dq[3]] 14 | 15 | set_property PACKAGE_PIN Y23 [get_ports uart_tx] 16 | set_property IOSTANDARD LVCMOS33 [get_ports uart_tx] 17 | set_property PACKAGE_PIN Y20 [get_ports uart_rx] 18 | set_property IOSTANDARD LVCMOS33 [get_ports uart_rx] 19 | 20 | set_property PACKAGE_PIN AE24 [get_ports sd_reset] 21 | set_property IOSTANDARD LVCMOS33 [get_ports sd_reset] 22 | set_property PACKAGE_PIN P28 [get_ports sd_cd] 23 | set_property IOSTANDARD LVCMOS33 [get_ports sd_cd] 24 | set_property PACKAGE_PIN R28 [get_ports sd_sck] 25 | set_property IOSTANDARD LVCMOS33 [get_ports sd_sck] 26 | set_property PACKAGE_PIN R29 [get_ports sd_cmd] 27 | set_property IOSTANDARD LVCMOS33 [get_ports sd_cmd] 28 | set_property PACKAGE_PIN R26 [get_ports sd_dat[0]] 29 | set_property IOSTANDARD LVCMOS33 [get_ports sd_dat[0]] 30 | set_property PACKAGE_PIN R30 [get_ports sd_dat[1]] 31 | set_property IOSTANDARD LVCMOS33 [get_ports sd_dat[1]] 32 | set_property PACKAGE_PIN P29 [get_ports sd_dat[2]] 33 | set_property IOSTANDARD LVCMOS33 [get_ports sd_dat[2]] 34 | set_property PACKAGE_PIN T30 [get_ports sd_dat[3]] 35 | set_property IOSTANDARD LVCMOS33 [get_ports sd_dat[3]] 36 | 37 | set_property PACKAGE_PIN AF12 [get_ports mdc] 38 | set_property IOSTANDARD LVCMOS15 [get_ports mdc] 39 | set_property PACKAGE_PIN AG12 [get_ports mdio] 40 | set_property IOSTANDARD LVCMOS15 [get_ports mdio] 41 | set_property PACKAGE_PIN AJ14 [get_ports rgmii_rd[0]] 42 | set_property IOSTANDARD LVCMOS15 [get_ports rgmii_rd[0]] 43 | set_property PACKAGE_PIN AH14 [get_ports rgmii_rd[1]] 44 | set_property IOSTANDARD LVCMOS15 [get_ports rgmii_rd[1]] 45 | set_property PACKAGE_PIN AK13 [get_ports rgmii_rd[2]] 46 | set_property IOSTANDARD LVCMOS15 [get_ports rgmii_rd[2]] 47 | set_property PACKAGE_PIN AJ13 [get_ports rgmii_rd[3]] 48 | set_property IOSTANDARD LVCMOS15 [get_ports rgmii_rd[3]] 49 | set_property PACKAGE_PIN AH11 [get_ports rgmii_rx_ctl] 50 | set_property IOSTANDARD LVCMOS15 [get_ports rgmii_rx_ctl] 51 | set_property PACKAGE_PIN AG10 [get_ports rgmii_rxc] 52 | set_property IOSTANDARD LVCMOS15 [get_ports rgmii_rxc] 53 | set_property PACKAGE_PIN AJ12 [get_ports rgmii_td[0]] 54 | set_property IOSTANDARD LVCMOS15 [get_ports rgmii_td[0]] 55 | set_property PACKAGE_PIN AK11 [get_ports rgmii_td[1]] 56 | set_property IOSTANDARD LVCMOS15 [get_ports rgmii_td[1]] 57 | set_property PACKAGE_PIN AJ11 [get_ports rgmii_td[2]] 58 | set_property IOSTANDARD LVCMOS15 [get_ports rgmii_td[2]] 59 | set_property PACKAGE_PIN AK10 [get_ports rgmii_td[3]] 60 | set_property IOSTANDARD LVCMOS15 [get_ports rgmii_td[3]] 61 | set_property PACKAGE_PIN AK14 [get_ports rgmii_tx_ctl] 62 | set_property IOSTANDARD LVCMOS15 [get_ports rgmii_tx_ctl] 63 | set_property PACKAGE_PIN AE10 [get_ports rgmii_txc] 64 | set_property IOSTANDARD LVCMOS15 [get_ports rgmii_txc] 65 | set_property PACKAGE_PIN AH24 [get_ports phy_rst_n] 66 | set_property IOSTANDARD LVCMOS33 [get_ports phy_rst_n] 67 | set_property PACKAGE_PIN AK16 [get_ports phy_irq_i] 68 | set_property IOSTANDARD LVCMOS18 [get_ports phy_irq_i] 69 | 70 | # set_property PACKAGE_PIN Y24 [get_ports hdmi_tx_cec] 71 | # set_property IOSTANDARD LVCMOS33 [get_ports hdmi_tx_cec] 72 | set_property PACKAGE_PIN AB20 [get_ports hdmi_tx_clk_n] 73 | set_property IOSTANDARD TMDS_33 [get_ports hdmi_tx_clk_n] 74 | set_property PACKAGE_PIN AA20 [get_ports hdmi_tx_clk_p] 75 | set_property IOSTANDARD TMDS_33 [get_ports hdmi_tx_clk_p] 76 | # set_property PACKAGE_PIN AG29 [get_ports hdmi_tx_hpd] 77 | # set_property IOSTANDARD LVCMOS33 [get_ports hdmi_tx_hpd] 78 | # set_property PACKAGE_PIN AF27 [get_ports hdmi_tx_scl] 79 | # set_property IOSTANDARD LVCMOS33 [get_ports hdmi_tx_scl] 80 | # set_property PACKAGE_PIN AF26 [get_ports hdmi_tx_sda] 81 | # set_property IOSTANDARD LVCMOS33 [get_ports hdmi_tx_sda] 82 | set_property PACKAGE_PIN AC21 [get_ports hdmi_tx_n[0]] 83 | set_property IOSTANDARD TMDS_33 [get_ports hdmi_tx_n[0]] 84 | set_property PACKAGE_PIN AC20 [get_ports hdmi_tx_p[0]] 85 | set_property IOSTANDARD TMDS_33 [get_ports hdmi_tx_p[0]] 86 | set_property PACKAGE_PIN AA23 [get_ports hdmi_tx_n[1]] 87 | set_property IOSTANDARD TMDS_33 [get_ports hdmi_tx_n[1]] 88 | set_property PACKAGE_PIN AA22 [get_ports hdmi_tx_p[1]] 89 | set_property IOSTANDARD TMDS_33 [get_ports hdmi_tx_p[1]] 90 | set_property PACKAGE_PIN AC25 [get_ports hdmi_tx_n[2]] 91 | set_property IOSTANDARD TMDS_33 [get_ports hdmi_tx_n[2]] 92 | set_property PACKAGE_PIN AB24 [get_ports hdmi_tx_p[2]] 93 | set_property IOSTANDARD TMDS_33 [get_ports hdmi_tx_p[2]] 94 | 95 | set_property PACKAGE_PIN AD23 [get_ports ps2_clk] 96 | set_property IOSTANDARD LVCMOS33 [get_ports ps2_clk] 97 | set_property PULLUP true [get_ports ps2_clk] 98 | set_property PACKAGE_PIN AE20 [get_ports ps2_dat] 99 | set_property IOSTANDARD LVCMOS33 [get_ports ps2_dat] 100 | set_property PULLUP true [get_ports ps2_dat] 101 | -------------------------------------------------------------------------------- /firmware/src/video.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | 3 | pub mod fbcon; 4 | 5 | const CR_ENABLE: usize = 0x0000; 6 | const CR_PXLFREQ: usize = 0x0004; 7 | const CR_POLARITY: usize = 0x0008; 8 | const CR_H_TOTAL: usize = 0x20; 9 | const CR_H_END_DISP: usize = 0x24; 10 | const CR_H_SRT_SYNC: usize = 0x28; 11 | const CR_H_END_SYNC: usize = 0x2C; 12 | const CR_V_TOTAL: usize = 0x30; 13 | const CR_V_END_DISP: usize = 0x34; 14 | const CR_V_SRT_SYNC: usize = 0x38; 15 | const CR_V_END_SYNC: usize = 0x3C; 16 | 17 | const CR_FB_COMMIT: usize = 0x40; 18 | const CR_FB_BASE: usize = 0x48; 19 | const CR_FB_WIDTH: usize = 0x50; 20 | const CR_FB_HEIGHT: usize = 0x54; 21 | const CR_FB_DEPTH: usize = 0x58; 22 | const CR_FB_BPL: usize = 0x5C; 23 | const CR_BG_COLOR: usize = 0x60; 24 | 25 | #[derive(Clone, Copy)] 26 | enum Polarity { 27 | Positive = 0, 28 | Negative = 1, 29 | } 30 | 31 | struct Mode { 32 | freq: u32, 33 | width: u32, 34 | hsync_start: u32, 35 | hsync_end: u32, 36 | htotal: u32, 37 | height: u32, 38 | vsync_start: u32, 39 | vsync_end: u32, 40 | vtotal: u32, 41 | hpol: Polarity, 42 | vpol: Polarity, 43 | } 44 | 45 | #[inline] 46 | fn reg(addr: usize) -> *mut u32 { 47 | (crate::address::DISPLAY_BASE + addr) as _ 48 | } 49 | 50 | fn set_mode(mode: &Mode) { 51 | // We currently have a fixed pixel clock at 74 MHz. 52 | // Check that the requested frequency is within 1% of that. 53 | assert!( 54 | mode.freq >= 73_260_000 && mode.freq <= 74_740_000, 55 | "Requested frequency of {} Hz cannot be achieved", 56 | mode.freq 57 | ); 58 | 59 | unsafe { 60 | // Disable display. 61 | core::ptr::write_volatile(reg(CR_ENABLE), 0); 62 | // Confirm that display is disabled. 63 | while core::ptr::read_volatile(reg(CR_ENABLE)) != 0 {} 64 | 65 | core::ptr::write_volatile(reg(CR_POLARITY), mode.hpol as u32 | (mode.vpol as u32) << 1); 66 | core::ptr::write_volatile(reg(CR_H_END_DISP), mode.width); 67 | core::ptr::write_volatile(reg(CR_H_SRT_SYNC), mode.hsync_start); 68 | core::ptr::write_volatile(reg(CR_H_END_SYNC), mode.hsync_end); 69 | core::ptr::write_volatile(reg(CR_H_TOTAL), mode.htotal); 70 | core::ptr::write_volatile(reg(CR_V_END_DISP), mode.height); 71 | core::ptr::write_volatile(reg(CR_V_SRT_SYNC), mode.vsync_start); 72 | core::ptr::write_volatile(reg(CR_V_END_SYNC), mode.vsync_end); 73 | core::ptr::write_volatile(reg(CR_V_TOTAL), mode.vtotal); 74 | } 75 | } 76 | 77 | fn turn_on() { 78 | unsafe { 79 | core::ptr::write_volatile(reg(CR_ENABLE), 1); 80 | } 81 | } 82 | 83 | const MODE_720P_60HZ: Mode = Mode { 84 | freq: 74_250_000, 85 | width: 1280, 86 | hsync_start: 1390, 87 | hsync_end: 1430, 88 | htotal: 1650, 89 | height: 720, 90 | vsync_start: 725, 91 | vsync_end: 730, 92 | vtotal: 750, 93 | hpol: Polarity::Positive, 94 | vpol: Polarity::Positive, 95 | }; 96 | 97 | const MODE_720P_30HZ: Mode = Mode { 98 | freq: 74_250_000, 99 | width: 1280, 100 | hsync_start: 3040, 101 | hsync_end: 3080, 102 | htotal: 3300, 103 | height: 720, 104 | vsync_start: 725, 105 | vsync_end: 730, 106 | vtotal: 750, 107 | hpol: Polarity::Positive, 108 | vpol: Polarity::Positive, 109 | }; 110 | 111 | const MODE_1080P_30HZ: Mode = Mode { 112 | freq: 74_250_000, 113 | width: 1920, 114 | hsync_start: 2008, 115 | hsync_end: 2052, 116 | htotal: 2200, 117 | height: 1080, 118 | vsync_start: 1084, 119 | vsync_end: 1089, 120 | vtotal: 1125, 121 | hpol: Polarity::Positive, 122 | vpol: Polarity::Positive, 123 | }; 124 | 125 | const MODE_1080P_60HZ: Mode = Mode { 126 | freq: 148_500_000, 127 | width: 1920, 128 | hsync_start: 2008, 129 | hsync_end: 2052, 130 | htotal: 2200, 131 | height: 1080, 132 | vsync_start: 1084, 133 | vsync_end: 1089, 134 | vtotal: 1125, 135 | hpol: Polarity::Positive, 136 | vpol: Polarity::Positive, 137 | }; 138 | 139 | #[cfg(feature = "fbcon")] 140 | static mut FBCON: Option> = None; 141 | 142 | #[cfg(feature = "fbcon")] 143 | pub unsafe fn get_fbcon() -> Option<&'static mut fbcon::Fbcon<'static>> { 144 | unsafe { FBCON.as_mut() } 145 | } 146 | 147 | pub fn init() { 148 | unsafe { 149 | core::ptr::write_volatile( 150 | reg(CR_FB_BASE) as *mut usize, 151 | crate::address::FRAMEBUFFER_BASE, 152 | ); 153 | core::ptr::write_volatile(reg(CR_FB_WIDTH), 1280); 154 | core::ptr::write_volatile(reg(CR_FB_HEIGHT), 720); 155 | // r5g6b5 156 | core::ptr::write_volatile(reg(CR_FB_DEPTH), 1); 157 | core::ptr::write_volatile(reg(CR_FB_BPL), 1280 * 2); 158 | core::ptr::write_volatile(reg(CR_FB_COMMIT), 1); 159 | } 160 | 161 | set_mode(&MODE_720P_30HZ); 162 | turn_on(); 163 | 164 | #[cfg(feature = "fbcon")] 165 | { 166 | let framebuffer = unsafe { 167 | core::slice::from_raw_parts_mut( 168 | crate::address::FRAMEBUFFER_BASE as *mut u8, 169 | 1280 * 720 * 2, 170 | ) 171 | }; 172 | let fb = fbcon::Framebuffer { 173 | framebuffer, 174 | width: 1280, 175 | height: 720, 176 | bpl: 1280 * 2, 177 | bpp: 2, 178 | }; 179 | unsafe { 180 | FBCON = Some(fbcon::Fbcon::new(fb)); 181 | } 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /genesys_2/util/vivado_create_ip.tcl: -------------------------------------------------------------------------------- 1 | set workroot [pwd] 2 | 3 | # Ensure that rgb2dvi IP is extracted 4 | if {!([file exists "${workroot}/ip/rgb2dvi"])} { 5 | exec mkdir -p "${workroot}/ip" 6 | exec tar -xvzf "${workroot}/rgb2dvi.tar" -C "${workroot}/ip" 7 | } 8 | 9 | # Ensure that axi_ps2 IP is extracted 10 | if {!([file exists "${workroot}/ip/axi_ps2_1.0"])} { 11 | exec mkdir -p "${workroot}/ip" 12 | exec tar -xvzf "${workroot}/axi_ps2_1.0.tar.gz" -C "${workroot}/ip" 13 | } 14 | 15 | set_property ip_repo_paths [file normalize "${workroot}/ip"] [current_project] 16 | update_ip_catalog 17 | 18 | set_property used_in_synthesis false [get_files clocks.xdc] 19 | 20 | # Enable Retiming in synthesis 21 | set_property STEPS.SYNTH_DESIGN.ARGS.RETIMING true [get_runs synth_1] 22 | 23 | # Create DDR Memory Controller 24 | create_ip -name mig_7series -vendor xilinx.com -library ip -module_name mig_7series_0 25 | set_property CONFIG.XML_INPUT_FILE [file normalize "${workroot}/mig.prj"] [get_ips mig_7series_0] 26 | 27 | # Create AXI Quad SPI 28 | create_ip -name axi_quad_spi -vendor xilinx.com -library ip -version 3.2 -module_name axi_quad_spi_0 29 | set_property -dict [list \ 30 | CONFIG.C_SPI_MEMORY {3} \ 31 | CONFIG.C_SPI_MEM_ADDR_BITS {32} \ 32 | CONFIG.C_USE_STARTUP {0} \ 33 | CONFIG.C_USE_STARTUP_INT {0} \ 34 | CONFIG.C_SPI_MODE {1} \ 35 | CONFIG.C_SCK_RATIO {2} \ 36 | CONFIG.C_XIP_MODE {1} \ 37 | CONFIG.C_XIP_PERF_MODE {0} \ 38 | CONFIG.C_TYPE_OF_AXI4_INTERFACE {1} \ 39 | CONFIG.C_S_AXI4_ID_WIDTH {1} \ 40 | CONFIG.Async_Clk {1} \ 41 | ] [get_ips axi_quad_spi_0] 42 | 43 | # Create AXI UART16550 44 | # This IP has a requirement for its input clock so we use io_clk instead of bus clk 45 | # and need an extra clock converter. 46 | # We also need a clock wizard for a 18.432MHz clock which works better for most baud rates. 47 | create_ip -name axi_uart16550 -vendor xilinx.com -library ip -version 2.0 -module_name axi_uart16550_0 48 | set_property -dict [list \ 49 | CONFIG.C_S_AXI_ACLK_FREQ_HZ_d {50} \ 50 | CONFIG.C_HAS_EXTERNAL_XIN {1} \ 51 | CONFIG.C_EXTERNAL_XIN_CLK_HZ_d {18.432} \ 52 | CONFIG.C_S_AXI_ACLK_FREQ_HZ {50000000} \ 53 | CONFIG.C_EXTERNAL_XIN_CLK_HZ {18432000} \ 54 | ] [get_ips axi_uart16550_0] 55 | 56 | create_ip -name axi_clock_converter -vendor xilinx.com -library ip -version 2.1 -module_name axi_clock_converter_uart 57 | set_property -dict [list \ 58 | CONFIG.PROTOCOL {AXI4LITE} \ 59 | CONFIG.ADDR_WIDTH {13} \ 60 | CONFIG.DATA_WIDTH {32} \ 61 | CONFIG.ID_WIDTH {0} \ 62 | CONFIG.AWUSER_WIDTH {0} \ 63 | CONFIG.ARUSER_WIDTH {0} \ 64 | CONFIG.RUSER_WIDTH {0} \ 65 | CONFIG.WUSER_WIDTH {0} \ 66 | CONFIG.BUSER_WIDTH {0} \ 67 | ] [get_ips axi_clock_converter_uart] 68 | 69 | create_ip -name clk_wiz -vendor xilinx.com -library ip -version 6.0 -module_name clk_wiz_uart 70 | set_property -dict [list \ 71 | CONFIG.PRIMITIVE {PLL} \ 72 | CONFIG.PRIM_SOURCE {No_buffer} \ 73 | CONFIG.PRIM_IN_FREQ {50} \ 74 | CONFIG.CLKOUT1_REQUESTED_OUT_FREQ {18.432} \ 75 | CONFIG.RESET_TYPE {ACTIVE_LOW} \ 76 | CONFIG.CLKIN1_JITTER_PS {200.0} \ 77 | CONFIG.CLKOUT1_DRIVES {BUFG} \ 78 | CONFIG.CLKOUT2_DRIVES {BUFG} \ 79 | CONFIG.CLKOUT3_DRIVES {BUFG} \ 80 | CONFIG.CLKOUT4_DRIVES {BUFG} \ 81 | CONFIG.CLKOUT5_DRIVES {BUFG} \ 82 | CONFIG.CLKOUT6_DRIVES {BUFG} \ 83 | CONFIG.CLKOUT7_DRIVES {BUFG} \ 84 | CONFIG.MMCM_DIVCLK_DIVIDE {2} \ 85 | CONFIG.MMCM_BANDWIDTH {OPTIMIZED} \ 86 | CONFIG.MMCM_CLKFBOUT_MULT_F {59} \ 87 | CONFIG.MMCM_CLKIN1_PERIOD {20.000} \ 88 | CONFIG.MMCM_CLKIN2_PERIOD {10.0} \ 89 | CONFIG.MMCM_COMPENSATION {ZHOLD} \ 90 | CONFIG.MMCM_CLKOUT0_DIVIDE_F {80} \ 91 | CONFIG.RESET_PORT {resetn} \ 92 | CONFIG.CLKOUT1_JITTER {180.940} \ 93 | CONFIG.CLKOUT1_PHASE_ERROR {220.889} \ 94 | ] [get_ips clk_wiz_uart] 95 | 96 | create_ip -name clk_wiz -vendor xilinx.com -library ip -version 6.0 -module_name clk_wiz_dvi 97 | set_property -dict [list \ 98 | CONFIG.PRIMITIVE {PLL} \ 99 | CONFIG.PRIM_SOURCE {No_buffer} \ 100 | CONFIG.PRIM_IN_FREQ {50} \ 101 | CONFIG.CLKOUT2_USED {true} \ 102 | CONFIG.CLKOUT1_REQUESTED_OUT_FREQ {74.25} \ 103 | CONFIG.CLKOUT2_REQUESTED_OUT_FREQ {371.25} \ 104 | CONFIG.USE_LOCKED {false} \ 105 | CONFIG.RESET_TYPE {ACTIVE_LOW} \ 106 | CONFIG.CLKIN1_JITTER_PS {200.0} \ 107 | CONFIG.CLKOUT1_DRIVES {BUFG} \ 108 | CONFIG.CLKOUT2_DRIVES {BUFG} \ 109 | CONFIG.CLKOUT3_DRIVES {BUFG} \ 110 | CONFIG.CLKOUT4_DRIVES {BUFG} \ 111 | CONFIG.CLKOUT5_DRIVES {BUFG} \ 112 | CONFIG.CLKOUT6_DRIVES {BUFG} \ 113 | CONFIG.CLKOUT7_DRIVES {BUFG} \ 114 | CONFIG.MMCM_DIVCLK_DIVIDE {2} \ 115 | CONFIG.MMCM_BANDWIDTH {OPTIMIZED} \ 116 | CONFIG.MMCM_CLKFBOUT_MULT_F {37} \ 117 | CONFIG.MMCM_CLKIN1_PERIOD {20.000} \ 118 | CONFIG.MMCM_CLKIN2_PERIOD {10.0} \ 119 | CONFIG.MMCM_COMPENSATION {ZHOLD} \ 120 | CONFIG.MMCM_CLKOUT0_DIVIDE_F {25} \ 121 | CONFIG.MMCM_CLKOUT1_DIVIDE {5} \ 122 | CONFIG.NUM_OUT_CLKS {2} \ 123 | CONFIG.RESET_PORT {resetn} \ 124 | CONFIG.CLKOUT1_JITTER {95.261} \ 125 | CONFIG.CLKOUT1_PHASE_ERROR {107.074} \ 126 | CONFIG.CLKOUT2_JITTER {76.746} \ 127 | CONFIG.CLKOUT2_PHASE_ERROR {107.074} \ 128 | ] [get_ips clk_wiz_dvi] 129 | 130 | create_ip -name rgb2dvi -vendor digilentinc.com -library ip -version 1.4 -module_name rgb2dvi_0 131 | set_property -dict [list \ 132 | CONFIG.kRstActiveHigh {false} \ 133 | CONFIG.kGenerateSerialClk {false} \ 134 | ] [get_ips rgb2dvi_0] 135 | 136 | create_ip -name axi_ps2 -vendor digilentinc.com -library IP -version 1.0 -module_name axi_ps2_0 137 | -------------------------------------------------------------------------------- /nexys_video/util/vivado_create_ip.tcl: -------------------------------------------------------------------------------- 1 | set workroot [pwd] 2 | 3 | # Ensure that rgb2dvi IP is extracted 4 | if {!([file exists "${workroot}/ip/rgb2dvi"])} { 5 | exec mkdir -p "${workroot}/ip" 6 | exec tar -xvzf "${workroot}/rgb2dvi.tar" -C "${workroot}/ip" 7 | } 8 | 9 | # Ensure that axi_ps2 IP is extracted 10 | if {!([file exists "${workroot}/ip/axi_ps2_1.0"])} { 11 | exec mkdir -p "${workroot}/ip" 12 | exec tar -xvzf "${workroot}/axi_ps2_1.0.tar.gz" -C "${workroot}/ip" 13 | } 14 | 15 | set_property ip_repo_paths [file normalize "${workroot}/ip"] [current_project] 16 | update_ip_catalog 17 | 18 | set_property used_in_synthesis false [get_files clocks.xdc] 19 | 20 | # Enable Retiming in synthesis 21 | set_property STEPS.SYNTH_DESIGN.ARGS.RETIMING true [get_runs synth_1] 22 | 23 | # Create DDR Memory Controller 24 | create_ip -name mig_7series -vendor xilinx.com -library ip -module_name mig_7series_0 25 | set_property CONFIG.XML_INPUT_FILE [file normalize "${workroot}/mig.prj"] [get_ips mig_7series_0] 26 | 27 | # Create AXI Quad SPI 28 | create_ip -name axi_quad_spi -vendor xilinx.com -library ip -version 3.2 -module_name axi_quad_spi_0 29 | set_property -dict [list \ 30 | CONFIG.C_SPI_MEMORY {3} \ 31 | CONFIG.C_SPI_MEM_ADDR_BITS {32} \ 32 | CONFIG.C_USE_STARTUP {0} \ 33 | CONFIG.C_USE_STARTUP_INT {0} \ 34 | CONFIG.C_SPI_MODE {1} \ 35 | CONFIG.C_SCK_RATIO {2} \ 36 | CONFIG.C_XIP_MODE {1} \ 37 | CONFIG.C_XIP_PERF_MODE {0} \ 38 | CONFIG.C_TYPE_OF_AXI4_INTERFACE {1} \ 39 | CONFIG.C_S_AXI4_ID_WIDTH {1} \ 40 | CONFIG.Async_Clk {1} \ 41 | ] [get_ips axi_quad_spi_0] 42 | 43 | # Create AXI UART16550 44 | # This IP has a requirement for its input clock so we use io_clk instead of bus clk 45 | # and need an extra clock converter. 46 | # We also need a clock wizard for a 18.432MHz clock which works better for most baud rates. 47 | create_ip -name axi_uart16550 -vendor xilinx.com -library ip -version 2.0 -module_name axi_uart16550_0 48 | set_property -dict [list \ 49 | CONFIG.C_S_AXI_ACLK_FREQ_HZ_d {50} \ 50 | CONFIG.C_HAS_EXTERNAL_XIN {1} \ 51 | CONFIG.C_EXTERNAL_XIN_CLK_HZ_d {18.432} \ 52 | CONFIG.C_S_AXI_ACLK_FREQ_HZ {50000000} \ 53 | CONFIG.C_EXTERNAL_XIN_CLK_HZ {18432000} \ 54 | ] [get_ips axi_uart16550_0] 55 | 56 | create_ip -name axi_clock_converter -vendor xilinx.com -library ip -version 2.1 -module_name axi_clock_converter_uart 57 | set_property -dict [list \ 58 | CONFIG.PROTOCOL {AXI4LITE} \ 59 | CONFIG.ADDR_WIDTH {13} \ 60 | CONFIG.DATA_WIDTH {32} \ 61 | CONFIG.ID_WIDTH {0} \ 62 | CONFIG.AWUSER_WIDTH {0} \ 63 | CONFIG.ARUSER_WIDTH {0} \ 64 | CONFIG.RUSER_WIDTH {0} \ 65 | CONFIG.WUSER_WIDTH {0} \ 66 | CONFIG.BUSER_WIDTH {0} \ 67 | ] [get_ips axi_clock_converter_uart] 68 | 69 | create_ip -name clk_wiz -vendor xilinx.com -library ip -version 6.0 -module_name clk_wiz_uart 70 | set_property -dict [list \ 71 | CONFIG.PRIMITIVE {PLL} \ 72 | CONFIG.PRIM_SOURCE {No_buffer} \ 73 | CONFIG.PRIM_IN_FREQ {50} \ 74 | CONFIG.CLKOUT1_REQUESTED_OUT_FREQ {18.432} \ 75 | CONFIG.RESET_TYPE {ACTIVE_LOW} \ 76 | CONFIG.CLKIN1_JITTER_PS {200.0} \ 77 | CONFIG.CLKOUT1_DRIVES {BUFG} \ 78 | CONFIG.CLKOUT2_DRIVES {BUFG} \ 79 | CONFIG.CLKOUT3_DRIVES {BUFG} \ 80 | CONFIG.CLKOUT4_DRIVES {BUFG} \ 81 | CONFIG.CLKOUT5_DRIVES {BUFG} \ 82 | CONFIG.CLKOUT6_DRIVES {BUFG} \ 83 | CONFIG.CLKOUT7_DRIVES {BUFG} \ 84 | CONFIG.MMCM_DIVCLK_DIVIDE {2} \ 85 | CONFIG.MMCM_BANDWIDTH {OPTIMIZED} \ 86 | CONFIG.MMCM_CLKFBOUT_MULT_F {59} \ 87 | CONFIG.MMCM_CLKIN1_PERIOD {20.000} \ 88 | CONFIG.MMCM_CLKIN2_PERIOD {10.0} \ 89 | CONFIG.MMCM_COMPENSATION {ZHOLD} \ 90 | CONFIG.MMCM_CLKOUT0_DIVIDE_F {80} \ 91 | CONFIG.RESET_PORT {resetn} \ 92 | CONFIG.CLKOUT1_JITTER {180.940} \ 93 | CONFIG.CLKOUT1_PHASE_ERROR {220.889} \ 94 | ] [get_ips clk_wiz_uart] 95 | 96 | create_ip -name clk_wiz -vendor xilinx.com -library ip -version 6.0 -module_name clk_wiz_dvi 97 | set_property -dict [list \ 98 | CONFIG.PRIMITIVE {PLL} \ 99 | CONFIG.PRIM_SOURCE {No_buffer} \ 100 | CONFIG.PRIM_IN_FREQ {50} \ 101 | CONFIG.CLKOUT2_USED {true} \ 102 | CONFIG.CLKOUT1_REQUESTED_OUT_FREQ {74.25} \ 103 | CONFIG.CLKOUT2_REQUESTED_OUT_FREQ {371.25} \ 104 | CONFIG.USE_LOCKED {false} \ 105 | CONFIG.RESET_TYPE {ACTIVE_LOW} \ 106 | CONFIG.CLKIN1_JITTER_PS {200.0} \ 107 | CONFIG.CLKOUT1_DRIVES {BUFG} \ 108 | CONFIG.CLKOUT2_DRIVES {BUFG} \ 109 | CONFIG.CLKOUT3_DRIVES {BUFG} \ 110 | CONFIG.CLKOUT4_DRIVES {BUFG} \ 111 | CONFIG.CLKOUT5_DRIVES {BUFG} \ 112 | CONFIG.CLKOUT6_DRIVES {BUFG} \ 113 | CONFIG.CLKOUT7_DRIVES {BUFG} \ 114 | CONFIG.MMCM_DIVCLK_DIVIDE {2} \ 115 | CONFIG.MMCM_BANDWIDTH {OPTIMIZED} \ 116 | CONFIG.MMCM_CLKFBOUT_MULT_F {37} \ 117 | CONFIG.MMCM_CLKIN1_PERIOD {20.000} \ 118 | CONFIG.MMCM_CLKIN2_PERIOD {10.0} \ 119 | CONFIG.MMCM_COMPENSATION {ZHOLD} \ 120 | CONFIG.MMCM_CLKOUT0_DIVIDE_F {25} \ 121 | CONFIG.MMCM_CLKOUT1_DIVIDE {5} \ 122 | CONFIG.NUM_OUT_CLKS {2} \ 123 | CONFIG.RESET_PORT {resetn} \ 124 | CONFIG.CLKOUT1_JITTER {95.261} \ 125 | CONFIG.CLKOUT1_PHASE_ERROR {107.074} \ 126 | CONFIG.CLKOUT2_JITTER {76.746} \ 127 | CONFIG.CLKOUT2_PHASE_ERROR {107.074} \ 128 | ] [get_ips clk_wiz_dvi] 129 | 130 | create_ip -name rgb2dvi -vendor digilentinc.com -library ip -version 1.4 -module_name rgb2dvi_0 131 | set_property -dict [list \ 132 | CONFIG.kRstActiveHigh {false} \ 133 | CONFIG.kGenerateSerialClk {false} \ 134 | ] [get_ips rgb2dvi_0] 135 | 136 | create_ip -name axi_ps2 -vendor digilentinc.com -library IP -version 1.0 -module_name axi_ps2_0 137 | --------------------------------------------------------------------------------