diff --git a/fpga/Makefile b/fpga/Makefile index 1437d8bc76..16576eef80 100644 --- a/fpga/Makefile +++ b/fpga/Makefile @@ -59,6 +59,20 @@ ifeq ($(SUB_PROJECT),arty) FPGA_BRAND ?= xilinx endif +ifeq ($(SUB_PROJECT),vc709) + SBT_PROJECT ?= fpga_platforms + MODEL ?= VC709FPGATestHarness + VLOG_MODEL ?= VC709FPGATestHarness + MODEL_PACKAGE ?= chipyard.fpga.vc709 + CONFIG ?= RocketVC709Config + CONFIG_PACKAGE ?= chipyard.fpga.vc709 + GENERATOR_PACKAGE ?= chipyard + TB ?= none # unused + TOP ?= ChipTop + BOARD ?= vc709 + FPGA_BRAND ?= xilinx +endif + include $(base_dir)/variables.mk # default variables to build the arty example @@ -71,6 +85,7 @@ default: $(mcs) # misc. directories ######################################################################################### fpga_dir := $(base_dir)/fpga/fpga-shells/$(FPGA_BRAND) +fpga_board_script_dir := $(fpga_dir)/$(BOARD)/tcl fpga_common_script_dir := $(fpga_dir)/common/tcl ######################################################################################### @@ -120,9 +135,19 @@ debug-bitstream: $(build_dir)/obj/post_synth.dcp $(build_dir)/debug_obj \ $(fpga_common_script_dir) +MCS_FILE := $(build_dir)/obj/$(MODEL).mcs +$(MCS_FILE): $(BIT_FILE) + cd $(build_dir); vivado \ + -nojournal -mode batch \ + -source $(fpga_common_script_dir)/write_cfgmem.tcl \ + -tclargs $(BOARD) $@ $^ \ + +.PHONY: mcs +mcs: $(MCS_FILE) + ######################################################################################### # general cleanup rules ######################################################################################### .PHONY: clean clean: - rm -rf $(gen_dir) + rm -rf $(build_dir) diff --git a/fpga/README.md b/fpga/README.md new file mode 100644 index 0000000000..0e4ea37317 --- /dev/null +++ b/fpga/README.md @@ -0,0 +1,50 @@ +# Guide + + +``` +$ export RISCV_LINUX=~/riscv-linux +$ export BUILDS=$(RISCV_LINUX)/builds +$ export OPENSBI=$(RISCV_LINUX)/opensbi +$ export LINUX=$(RISCV_LINUX)/linux +$ export CHIPYARD=~/chipyard +$ export gen_src=$(CHIPYARD)/fpga/generated-src +$ export RocketVC709Config=chipyard.fpga.vc709.VC709FPGATestHarness.RocketVC709Config +$ export BoomVC709Config=chipyard.fpga.vc709.VC709FPGATestHarness.BoomVC709Config +``` +Build Linux kernel +``` +$ make ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- menuconfig +$ make -j8 ARCH=riscv CROSS_COMPILE=riscv64-unknown-linux-gnu- +``` +Convert *.dts files into *.dtb format. +``` +$ cd $RISCV_LINUX/$BUILDS +$ cp -p $gen_src/$RocketVC709Config/$(RocketVC709Config).dts $BUILDS +$ dtc -I dts -O dtb -o $(RocketVC709Config).dtb $(RocketVC709Config).dts +$ cp -p $gen_src/$BoomVC709Config/$(BoomVC709Config).dts $BUILDS +$ dtc -I dts -O dtb -o $(BoomVC709Config).dtb $(BoomVC709Config).dts +``` +Build Open SBI for RocketVC709Config. +``` +make PLATFORM=generic CROSS_COMPILE=riscv64-unknown-linux-gnu- PLATFORM_RISCV_XLEN=64 FW_PAYLOAD=y FW_PAYLOAD_PATH=~/riscv-linux/linux/arch/riscv/boot/Image FW_FDT_PATH=$BUILDS/$(RocketVC709Config).dtb clean +make PLATFORM=generic CROSS_COMPILE=riscv64-unknown-linux-gnu- PLATFORM_RISCV_XLEN=64 FW_PAYLOAD=y FW_PAYLOAD_PATH=~/riscv-linux/linux/arch/riscv/boot/Image FW_FDT_PATH=$BUILDS/$(RocketVC709Config).dtb install +``` +Build Open SBI for BoomVC709Config. +``` +make PLATFORM=generic CROSS_COMPILE=riscv64-unknown-linux-gnu- PLATFORM_RISCV_XLEN=64 FW_PAYLOAD=y FW_PAYLOAD_PATH=~/riscv-linux/linux/arch/riscv/boot/Image FW_FDT_PATH=$BUILDS/$(BoomVC709Config).dtb clean +make PLATFORM=generic CROSS_COMPILE=riscv64-unknown-linux-gnu- PLATFORM_RISCV_XLEN=64 FW_PAYLOAD=y FW_PAYLOAD_PATH=~/riscv-linux/linux/arch/riscv/boot/Image FW_FDT_PATH=$BUILDS/$(BoomVC709Config).dtb install +``` +Find the `ttyUSB*` device. The outputs looks like this: `[79643.136986] usb 1-9: cp210x converter now attached to ttyUSB2`. +``` +$ sudo dmesg | grep tty +``` +Download `fw_payload.bin` to the board, then start up the kernel. +``` +$ sudo ./serial /dev/ttyUSB2 0x80000000 ../opensbi/build/platform/generic/firmware/fw_payload.bin +$ sudo ./serial /dev/ttyUSB2 +``` + +``` +$ make PLATFORM=generic CROSS_COMPILE=riscv64-unknown-linux-gnu- FW_PAYLOAD_PATH=../linux/arch/riscv/boot/Image install +$ qemu-system-riscv64 -M virt -m 256M -nographic -bios opensbi/build/platform/generic/firmware/fw_jump.bin -kernel ./linux/arch/riscv/boot/Image -drive file=./rootfs.img,format=raw,id=hd0 -device virtio-blk-device,drive=hd0 -append "root=/dev/vda rw console=ttyS0" +``` \ No newline at end of file diff --git a/fpga/src/main/resources/vc709/uartboot/Makefile b/fpga/src/main/resources/vc709/uartboot/Makefile new file mode 100644 index 0000000000..794dac7dd2 --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/Makefile @@ -0,0 +1,42 @@ +# RISCV environment variable must be set +ROOT_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +BUILD_DIR := $(ROOT_DIR)/build + +CC=$(RISCV)/bin/riscv64-unknown-elf-gcc +OBJCOPY=$(RISCV)/bin/riscv64-unknown-elf-objcopy +OBJDUMP=$(RISCV)/bin/riscv64-unknown-elf-objdump +CFLAGS=-march=rv64ima -mcmodel=medany -O2 -std=gnu11 -Wall -nostartfiles +CFLAGS+= -fno-common -g -DENTROPY=0 -mabi=lp64 -DNONSMP_HART=0 +CFLAGS+= -I $(ROOT_DIR)/include -I. +LFLAGS=-static -nostdlib -L $(ROOT_DIR)/linker -T bootrom.elf.lds + +PBUS_CLK ?= 1000000 # default to 1MHz but really should be overridden + +default: elf bin dump + +elf := $(BUILD_DIR)/bootrom.elf +$(elf): head.S crc16.c kprintf.c serial.c + mkdir -p $(BUILD_DIR) + $(CC) $(CFLAGS) -DTL_CLK="$(PBUS_CLK)UL" $(LFLAGS) -o $@ head.S crc16.c kprintf.c serial.c + +.PHONY: elf +elf: $(elf) + +bin := $(BUILD_DIR)/bootrom.bin +$(bin): $(elf) + mkdir -p $(BUILD_DIR) + $(OBJCOPY) -O binary --change-addresses=-0x10000 $< $@ + +.PHONY: bin +bin: $(bin) + +dump := $(BUILD_DIR)/bootrom.dump +$(dump): $(elf) + $(OBJDUMP) -D -S $< > $@ + +.PHONY: dump +dump: $(dump) + +.PHONY: clean +clean:: + rm -rf $(BUILD_DIR) diff --git a/fpga/src/main/resources/vc709/uartboot/Makefile.mk b/fpga/src/main/resources/vc709/uartboot/Makefile.mk new file mode 100644 index 0000000000..47694a0ef5 --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/Makefile.mk @@ -0,0 +1,72 @@ +# RISCV environment variable must be set + +CC=$(RISCV)/bin/riscv64-unknown-elf-gcc +OBJCOPY=$(RISCV)/bin/riscv64-unknown-elf-objcopy +CFLAGS=-march=rv64imac -mcmodel=medany -O2 -std=gnu11 -Wall -nostartfiles +CFLAGS+= -fno-common -g -DENTROPY=0 -mabi=lp64 -DNONSMP_HART=0 +CFLAGS+= -I $(BOOTROM_DIR)/include -I. +LFLAGS=-static -nostdlib -L $(BOOTROM_DIR)/linker -T bootrom.elf.lds + +dts := $(BUILD_DIR)/$(CONFIG_PROJECT).$(CONFIG).dts +dtb := $(BUILD_DIR)/$(CONFIG_PROJECT).$(CONFIG).dtb +clk := $(BUILD_DIR)/$(CONFIG_PROJECT).$(CONFIG).tl_clock.h + +## device tree +$(clk): $(dts) + awk '/tlclk {/ && !f{f=1; next}; f && match($$0, /^.*clock-frequency.*<(.*)>.*/, arr) { print "#define TL_CLK " arr[1] "UL"}' $< > $@.tmp + mv $@.tmp $@ + +$(dtb): $(dts) + dtc -I dts -O dtb -o $@ $< + +.PHONY: dtb +dtb: $(dtb) + +## uart_boot +elf := $(BUILD_DIR)/bootrom.elf +$(elf): $(dtb) head.S crc16.c kprintf.c serial.c $(clk) + $(CC) $(CFLAGS) -include $(clk) -DDEVICE_TREE='"$(dtb)"' $(LFLAGS) -o $@ head.S crc16.c kprintf.c serial.c + +## sd_boot +# elf := $(BUILD_DIR)/bootrom.elf +# $(elf): $(dtb) head.S kprintf.c sd.c $(clk) + # $(CC) $(CFLAGS) -include $(clk) -DDEVICE_TREE='"$(dtb)"' $(LFLAGS) -o $@ head.S sd.c kprintf.c + +.PHONY: elf +elf: $(elf) + +bin := $(BUILD_DIR)/bootrom.bin +$(bin): $(elf) + $(OBJCOPY) -O binary $< $@ + +.PHONY: bin +bin: $(bin) + +hex := $(BUILD_DIR)/bootrom.hex +$(hex): $(bin) + od -t x4 -An -w4 -v $< > $@ + +.PHONY: hex +hex: $(hex) + +# # Berkeley Boot Loader (BBL) +# elf := $(RISCV)/riscv64-unknown-elf/bin/bbl +# bin := $(BUILD_DIR)/bootrom.bin +# $(bin): $(elf) +# $(OBJCOPY) -O binary $< $@ + +# hex := $(BUILD_DIR)/bootrom.hex +# $(hex): $(bin) +# od -t x4 -An -w4 -v $< > $@ + +# Finally +romgen := $(BUILD_DIR)/rom.v +$(romgen): $(hex) + $(rocketchip_dir)/scripts/vlsi_rom_gen $(ROMCONF) $< > $@ + +.PHONY: romgen +romgen: $(romgen) + +.PHONY: clean +clean:: + rm -rf $(hex) $(elf) diff --git a/fpga/src/main/resources/vc709/uartboot/common.h b/fpga/src/main/resources/vc709/uartboot/common.h new file mode 100644 index 0000000000..4f71e103a8 --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/common.h @@ -0,0 +1,9 @@ +#ifndef _SDBOOT_COMMON_H +#define _SDBOOT_COMMON_H + +#ifndef PAYLOAD_DEST + #define PAYLOAD_DEST MEMORY_MEM_ADDR +#endif + + +#endif diff --git a/fpga/src/main/resources/vc709/uartboot/crc16.c b/fpga/src/main/resources/vc709/uartboot/crc16.c new file mode 100644 index 0000000000..7534375e24 --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/crc16.c @@ -0,0 +1,26 @@ +/* + * @,@Author: ,: your name + * @,@Date: ,: 1970-01-01 08:00:00 + * @,@LastEditTime: ,: 2021-01-30 11:48:26 + * @,@LastEditors: ,: Please set LastEditors + * @,@Description: ,: In User Settings Edit + * @,@FilePath: ,: /freedom/bootrom/sdboot/crc16.h + */ +#include "include/crc16.h" + +inline uint16_t crc16_round(uint16_t crc, uint8_t data) { + crc = (uint8_t)(crc >> 8) | (crc << 8); + crc ^= data; + crc ^= (uint8_t)(crc >> 4) & 0xf; + crc ^= crc << 12; + crc ^= (crc & 0xff) << 5; + return crc; +} + +uint16_t crc16(uint8_t *q) { + uint16_t crc = 0; + for (int i = 0; i < CRC16_LEN; i++) { + crc = crc16_round(crc, *q++); + } + return crc; +} \ No newline at end of file diff --git a/fpga/src/main/resources/vc709/uartboot/gpio.c b/fpga/src/main/resources/vc709/uartboot/gpio.c new file mode 100644 index 0000000000..159bede78f --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/gpio.c @@ -0,0 +1 @@ +#include "gpio.h" diff --git a/fpga/src/main/resources/vc709/uartboot/head.S b/fpga/src/main/resources/vc709/uartboot/head.S new file mode 100644 index 0000000000..c6653f7ceb --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/head.S @@ -0,0 +1,21 @@ +// See LICENSE.Sifive for license details. +#include +#include +#include "common.h" + + .section .text.init + .option norvc + .globl _prog_start +_prog_start: + smp_pause(s1, s2) + li sp, (PAYLOAD_DEST + 0xffff000) + call main + smp_resume(s1, s2) + csrr a0, mhartid // hartid for next level bootloader + la a1, dtb // dtb address for next level bootloader + li s1, PAYLOAD_DEST + jr s1 + + .section .dtb + .align 3 +dtb: diff --git a/fpga/src/main/resources/vc709/uartboot/hello.c b/fpga/src/main/resources/vc709/uartboot/hello.c new file mode 100644 index 0000000000..7e718ea256 --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/hello.c @@ -0,0 +1,14 @@ +#include +#include "./include/platform.h" +#include "./include/common.h" + +#define DEBUG +#include "kprintf.h" + +#define MAX_CORES 8 + +int main(void) +{ + kputs("this is hello\n"); + return 0; +} diff --git a/fpga/src/main/resources/vc709/uartboot/include/bits.h b/fpga/src/main/resources/vc709/uartboot/include/bits.h new file mode 100644 index 0000000000..bfe656feb0 --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/include/bits.h @@ -0,0 +1,36 @@ +// See LICENSE for license details. +#ifndef _RISCV_BITS_H +#define _RISCV_BITS_H + +#define likely(x) __builtin_expect((x), 1) +#define unlikely(x) __builtin_expect((x), 0) + +#define ROUNDUP(a, b) ((((a)-1)/(b)+1)*(b)) +#define ROUNDDOWN(a, b) ((a)/(b)*(b)) + +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define CLAMP(a, lo, hi) MIN(MAX(a, lo), hi) + +#define EXTRACT_FIELD(val, which) (((val) & (which)) / ((which) & ~((which)-1))) +#define INSERT_FIELD(val, which, fieldval) (((val) & ~(which)) | ((fieldval) * ((which) & ~((which)-1)))) + +#define STR(x) XSTR(x) +#define XSTR(x) #x + +#if __riscv_xlen == 64 +# define SLL32 sllw +# define STORE sd +# define LOAD ld +# define LWU lwu +# define LOG_REGBYTES 3 +#else +# define SLL32 sll +# define STORE sw +# define LOAD lw +# define LWU lw +# define LOG_REGBYTES 2 +#endif +#define REGBYTES (1 << LOG_REGBYTES) + +#endif diff --git a/fpga/src/main/resources/vc709/uartboot/include/const.h b/fpga/src/main/resources/vc709/uartboot/include/const.h new file mode 100644 index 0000000000..8dcffbb064 --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/include/const.h @@ -0,0 +1,18 @@ +// See LICENSE for license details. +/* Derived from */ + +#ifndef _SIFIVE_CONST_H +#define _SIFIVE_CONST_H + +#ifdef __ASSEMBLER__ +#define _AC(X,Y) X +#define _AT(T,X) X +#else +#define _AC(X,Y) (X##Y) +#define _AT(T,X) ((T)(X)) +#endif /* !__ASSEMBLER__*/ + +#define _BITUL(x) (_AC(1,UL) << (x)) +#define _BITULL(x) (_AC(1,ULL) << (x)) + +#endif /* _SIFIVE_CONST_H */ diff --git a/fpga/src/main/resources/vc709/uartboot/include/crc16.h b/fpga/src/main/resources/vc709/uartboot/include/crc16.h new file mode 100644 index 0000000000..fb63c02cdd --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/include/crc16.h @@ -0,0 +1,10 @@ +#include + +#define CRC16_BITS 12 +#define CRC16_LEN 4096 +#define NUM_BLOCKS 1024 +#define NAK 0x15 +#define ACK 0x06 + +uint16_t crc16_round(uint16_t crc, uint8_t data); +uint16_t crc16(uint8_t *q); \ No newline at end of file diff --git a/fpga/src/main/resources/vc709/uartboot/include/devices/clint.h b/fpga/src/main/resources/vc709/uartboot/include/devices/clint.h new file mode 100644 index 0000000000..c2b05baeda --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/include/devices/clint.h @@ -0,0 +1,14 @@ +// See LICENSE for license details. + +#ifndef _SIFIVE_CLINT_H +#define _SIFIVE_CLINT_H + + +#define CLINT_MSIP 0x0000 +#define CLINT_MSIP_size 0x4 +#define CLINT_MTIMECMP 0x4000 +#define CLINT_MTIMECMP_size 0x8 +#define CLINT_MTIME 0xBFF8 +#define CLINT_MTIME_size 0x8 + +#endif /* _SIFIVE_CLINT_H */ diff --git a/fpga/src/main/resources/vc709/uartboot/include/devices/gpio.h b/fpga/src/main/resources/vc709/uartboot/include/devices/gpio.h new file mode 100644 index 0000000000..f7f0acb40f --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/include/devices/gpio.h @@ -0,0 +1,24 @@ +// See LICENSE for license details. + +#ifndef _SIFIVE_GPIO_H +#define _SIFIVE_GPIO_H + +#define GPIO_INPUT_VAL (0x00) +#define GPIO_INPUT_EN (0x04) +#define GPIO_OUTPUT_EN (0x08) +#define GPIO_OUTPUT_VAL (0x0C) +#define GPIO_PULLUP_EN (0x10) +#define GPIO_DRIVE (0x14) +#define GPIO_RISE_IE (0x18) +#define GPIO_RISE_IP (0x1C) +#define GPIO_FALL_IE (0x20) +#define GPIO_FALL_IP (0x24) +#define GPIO_HIGH_IE (0x28) +#define GPIO_HIGH_IP (0x2C) +#define GPIO_LOW_IE (0x30) +#define GPIO_LOW_IP (0x34) +#define GPIO_IOF_EN (0x38) +#define GPIO_IOF_SEL (0x3C) +#define GPIO_OUTPUT_XOR (0x40) + +#endif /* _SIFIVE_GPIO_H */ diff --git a/fpga/src/main/resources/vc709/uartboot/include/devices/plic.h b/fpga/src/main/resources/vc709/uartboot/include/devices/plic.h new file mode 100644 index 0000000000..5894ebacf3 --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/include/devices/plic.h @@ -0,0 +1,31 @@ +// See LICENSE for license details. + +#ifndef PLIC_H +#define PLIC_H + +#include "../const.h" + +// 32 bits per source +#define PLIC_PRIORITY_OFFSET _AC(0x0000,UL) +#define PLIC_PRIORITY_SHIFT_PER_SOURCE 2 +// 1 bit per source (1 address) +#define PLIC_PENDING_OFFSET _AC(0x1000,UL) +#define PLIC_PENDING_SHIFT_PER_SOURCE 0 + +//0x80 per target +#define PLIC_ENABLE_OFFSET _AC(0x2000,UL) +#define PLIC_ENABLE_SHIFT_PER_TARGET 7 + + +#define PLIC_THRESHOLD_OFFSET _AC(0x200000,UL) +#define PLIC_CLAIM_OFFSET _AC(0x200004,UL) +#define PLIC_THRESHOLD_SHIFT_PER_TARGET 12 +#define PLIC_CLAIM_SHIFT_PER_TARGET 12 + +#define PLIC_MAX_SOURCE 1023 +#define PLIC_SOURCE_MASK 0x3FF + +#define PLIC_MAX_TARGET 15871 +#define PLIC_TARGET_MASK 0x3FFF + +#endif /* PLIC_H */ diff --git a/fpga/src/main/resources/vc709/uartboot/include/devices/spi.h b/fpga/src/main/resources/vc709/uartboot/include/devices/spi.h new file mode 100644 index 0000000000..7118572abc --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/include/devices/spi.h @@ -0,0 +1,79 @@ +// See LICENSE for license details. + +#ifndef _SIFIVE_SPI_H +#define _SIFIVE_SPI_H + +/* Register offsets */ + +#define SPI_REG_SCKDIV 0x00 +#define SPI_REG_SCKMODE 0x04 +#define SPI_REG_CSID 0x10 +#define SPI_REG_CSDEF 0x14 +#define SPI_REG_CSMODE 0x18 + +#define SPI_REG_DCSSCK 0x28 +#define SPI_REG_DSCKCS 0x2a +#define SPI_REG_DINTERCS 0x2c +#define SPI_REG_DINTERXFR 0x2e + +#define SPI_REG_FMT 0x40 +#define SPI_REG_TXFIFO 0x48 +#define SPI_REG_RXFIFO 0x4c +#define SPI_REG_TXCTRL 0x50 +#define SPI_REG_RXCTRL 0x54 + +#define SPI_REG_FCTRL 0x60 +#define SPI_REG_FFMT 0x64 + +#define SPI_REG_IE 0x70 +#define SPI_REG_IP 0x74 + +/* Fields */ + +#define SPI_SCK_POL 0x1 +#define SPI_SCK_PHA 0x2 + +#define SPI_FMT_PROTO(x) ((x) & 0x3) +#define SPI_FMT_ENDIAN(x) (((x) & 0x1) << 2) +#define SPI_FMT_DIR(x) (((x) & 0x1) << 3) +#define SPI_FMT_LEN(x) (((x) & 0xf) << 16) + +/* TXCTRL register */ +#define SPI_TXWM(x) ((x) & 0xffff) +/* RXCTRL register */ +#define SPI_RXWM(x) ((x) & 0xffff) + +#define SPI_IP_TXWM 0x1 +#define SPI_IP_RXWM 0x2 + +#define SPI_FCTRL_EN 0x1 + +#define SPI_INSN_CMD_EN 0x1 +#define SPI_INSN_ADDR_LEN(x) (((x) & 0x7) << 1) +#define SPI_INSN_PAD_CNT(x) (((x) & 0xf) << 4) +#define SPI_INSN_CMD_PROTO(x) (((x) & 0x3) << 8) +#define SPI_INSN_ADDR_PROTO(x) (((x) & 0x3) << 10) +#define SPI_INSN_DATA_PROTO(x) (((x) & 0x3) << 12) +#define SPI_INSN_CMD_CODE(x) (((x) & 0xff) << 16) +#define SPI_INSN_PAD_CODE(x) (((x) & 0xff) << 24) + +#define SPI_TXFIFO_FULL (1 << 31) +#define SPI_RXFIFO_EMPTY (1 << 31) + +/* Values */ + +#define SPI_CSMODE_AUTO 0 +#define SPI_CSMODE_HOLD 2 +#define SPI_CSMODE_OFF 3 + +#define SPI_DIR_RX 0 +#define SPI_DIR_TX 1 + +#define SPI_PROTO_S 0 +#define SPI_PROTO_D 1 +#define SPI_PROTO_Q 2 + +#define SPI_ENDIAN_MSB 0 +#define SPI_ENDIAN_LSB 1 + +#endif /* _SIFIVE_SPI_H */ diff --git a/fpga/src/main/resources/vc709/uartboot/include/devices/uart.h b/fpga/src/main/resources/vc709/uartboot/include/devices/uart.h new file mode 100644 index 0000000000..32d31b6846 --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/include/devices/uart.h @@ -0,0 +1,28 @@ +// See LICENSE for license details. + +#ifndef _SIFIVE_UART_H +#define _SIFIVE_UART_H + +/* Register offsets */ +#define UART_REG_TXFIFO 0x00 +#define UART_REG_RXFIFO 0x04 +#define UART_REG_TXCTRL 0x08 +#define UART_REG_RXCTRL 0x0c +#define UART_REG_IE 0x10 +#define UART_REG_IP 0x14 +#define UART_REG_DIV 0x18 + +/* TXCTRL register */ +#define UART_TXEN 0x1 +#define UART_TXNSTOP 0x2 +#define UART_TXWM(x) (((x) & 0xffff) << 16) + +/* RXCTRL register */ +#define UART_RXEN 0x1 +#define UART_RXWM(x) (((x) & 0xffff) << 16) + +/* IP register */ +#define UART_IP_TXWM 0x1 +#define UART_IP_RXWM 0x2 + +#endif /* _SIFIVE_UART_H */ \ No newline at end of file diff --git a/fpga/src/main/resources/vc709/uartboot/include/kprintf.h b/fpga/src/main/resources/vc709/uartboot/include/kprintf.h new file mode 100644 index 0000000000..c3bfb24cc8 --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/include/kprintf.h @@ -0,0 +1,68 @@ +// See LICENSE for license details. +#ifndef _SDBOOT_KPRINTF_H +#define _SDBOOT_KPRINTF_H + +#include + +#include "platform.h" + +#define REG32(p, i) ((p)[(i) >> 2]) + +#ifndef UART_CTRL_ADDR + #ifndef UART_NUM + #define UART_NUM 0 + #endif + + #define _CONCAT3(A, B, C) A ## B ## C + #define _UART_CTRL_ADDR(UART_NUM) _CONCAT3(UART, UART_NUM, _CTRL_ADDR) + #define UART_CTRL_ADDR _UART_CTRL_ADDR(UART_NUM) +#endif +static volatile uint32_t * const uart = (void *)(UART_CTRL_ADDR); + +static inline void kputc(char c) +{ + volatile uint32_t *tx = ®32(uart, UART_REG_TXFIFO); +#ifdef __riscv_atomic + int32_t r; + do { + __asm__ __volatile__ ( + "amoor.w %0, %2, %1\n" + : "=r" (r), "+A" (*tx) + : "r" (c)); + } while (r < 0); +#else + while ((int32_t)(*tx) < 0); + *tx = c; +#endif +} + +static inline char kgetc() +{ + int32_t val = -1; + volatile uint32_t *rx = ®32(uart, UART_REG_RXFIFO); + + while (val < 0) { + val = *rx; + } + + return val & 0xff; +} + + +extern void kputs(const char *); +extern void kprintf(const char *, ...); + +extern void kgets(char *); + +extern void kread(char *s, int count); +extern void kwrite(char *s, int count); + +#ifdef DEBUG +#define dprintf(s, ...) kprintf((s), ##__VA_ARGS__) +#define dputs(s) kputs((s)) +#else +#define dprintf(s, ...) do { } while (0) +#define dputs(s) do { } while (0) +#endif + +#endif /* _SDBOOT_KPRINTF_H */ diff --git a/fpga/src/main/resources/vc709/uartboot/include/platform.h b/fpga/src/main/resources/vc709/uartboot/include/platform.h new file mode 100644 index 0000000000..b60fc9b1b1 --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/include/platform.h @@ -0,0 +1,99 @@ +// See LICENSE for license details. + +#ifndef _SIFIVE_PLATFORM_H +#define _SIFIVE_PLATFORM_H + +#include "const.h" +#include "riscv_test_defaults.h" +#include "devices/clint.h" +#include "devices/gpio.h" +#include "devices/plic.h" +#include "devices/spi.h" +#include "devices/uart.h" + + // Some things missing from the official encoding.h +#if __riscv_xlen == 32 + #define MCAUSE_INT 0x80000000UL + #define MCAUSE_CAUSE 0x7FFFFFFFUL +#else + #define MCAUSE_INT 0x8000000000000000UL + #define MCAUSE_CAUSE 0x7FFFFFFFFFFFFFFFUL +#endif + +/**************************************************************************** + * Platform definitions + *****************************************************************************/ + +// CPU info +#define NUM_CORES 1 +#define GLOBAL_INT_SIZE 15 +#define GLOBAL_INT_MAX_PRIORITY 7 + +// Memory map +#define AXI_PCIE_HOST_1_00_A_CTRL_ADDR _AC(0x50000000,UL) +#define AXI_PCIE_HOST_1_00_A_CTRL_SIZE _AC(0x4000000,UL) +#define CLINT_CTRL_ADDR _AC(0x2000000,UL) +#define CLINT_CTRL_SIZE _AC(0x10000,UL) +#define DEBUG_CTRL_ADDR _AC(0x0,UL) +#define DEBUG_CTRL_SIZE _AC(0x1000,UL) +#define ERROR_MEM_ADDR _AC(0x3000,UL) +#define ERROR_MEM_SIZE _AC(0x1000,UL) +#define GPIO_CTRL_ADDR _AC(0x64002000,UL) +#define GPIO_CTRL_SIZE _AC(0x1000,UL) +#define MASKROM_MEM_ADDR _AC(0x10000,UL) +#define MASKROM_MEM_SIZE _AC(0x2000,UL) +#define MEMORY_MEM_ADDR _AC(0x80000000,UL) +#define MEMORY_MEM_SIZE _AC(0x100000000,UL) +#define PLIC_CTRL_ADDR _AC(0xc000000,UL) +#define PLIC_CTRL_SIZE _AC(0x4000000,UL) +#define SPI_CTRL_ADDR _AC(0x64001000,UL) +#define SPI_CTRL_SIZE _AC(0x1000,UL) +#define TEST_CTRL_ADDR _AC(0x4000,UL) +#define TEST_CTRL_SIZE _AC(0x1000,UL) +#define UART_CTRL_ADDR _AC(0x64000000,UL) +#define UART_CTRL_SIZE _AC(0x1000,UL) + +// IOF masks + + +// Interrupt numbers +#define UART_INT_BASE 1 +#define SPI_INT_BASE 2 +#define GPIO_INT_BASE 3 +#define AXI_PCIE_HOST_1_00_A_INT_BASE 7 + +// Helper functions +#define _REG64(p, i) (*(volatile uint64_t *)((p) + (i))) +#define _REG32(p, i) (*(volatile uint32_t *)((p) + (i))) +#define _REG16(p, i) (*(volatile uint16_t *)((p) + (i))) +// Bulk set bits in `reg` to either 0 or 1. +// E.g. SET_BITS(MY_REG, 0x00000007, 0) would generate MY_REG &= ~0x7 +// E.g. SET_BITS(MY_REG, 0x00000007, 1) would generate MY_REG |= 0x7 +#define SET_BITS(reg, mask, value) if ((value) == 0) { (reg) &= ~(mask); } else { (reg) |= (mask); } +#define AXI_PCIE_HOST_1_00_A_REG(offset) _REG32(AXI_PCIE_HOST_1_00_A_CTRL_ADDR, offset) +#define CLINT_REG(offset) _REG32(CLINT_CTRL_ADDR, offset) +#define DEBUG_REG(offset) _REG32(DEBUG_CTRL_ADDR, offset) +#define ERROR_REG(offset) _REG32(ERROR_CTRL_ADDR, offset) +#define GPIO_REG(offset) _REG32(GPIO_CTRL_ADDR, offset) +#define MASKROM_REG(offset) _REG32(MASKROM_CTRL_ADDR, offset) +#define MEMORY_REG(offset) _REG32(MEMORY_CTRL_ADDR, offset) +#define PLIC_REG(offset) _REG32(PLIC_CTRL_ADDR, offset) +#define SPI_REG(offset) _REG32(SPI_CTRL_ADDR, offset) +#define TEST_REG(offset) _REG32(TEST_CTRL_ADDR, offset) +#define UART_REG(offset) _REG32(UART_CTRL_ADDR, offset) +#define AXI_PCIE_HOST_1_00_A_REG64(offset) _REG64(AXI_PCIE_HOST_1_00_A_CTRL_ADDR, offset) +#define CLINT_REG64(offset) _REG64(CLINT_CTRL_ADDR, offset) +#define DEBUG_REG64(offset) _REG64(DEBUG_CTRL_ADDR, offset) +#define ERROR_REG64(offset) _REG64(ERROR_CTRL_ADDR, offset) +#define GPIO_REG64(offset) _REG64(GPIO_CTRL_ADDR, offset) +#define MASKROM_REG64(offset) _REG64(MASKROM_CTRL_ADDR, offset) +#define MEMORY_REG64(offset) _REG64(MEMORY_CTRL_ADDR, offset) +#define PLIC_REG64(offset) _REG64(PLIC_CTRL_ADDR, offset) +#define SPI_REG64(offset) _REG64(SPI_CTRL_ADDR, offset) +#define TEST_REG64(offset) _REG64(TEST_CTRL_ADDR, offset) +#define UART_REG64(offset) _REG64(UART_CTRL_ADDR, offset) + +// Misc + + +#endif /* _SIFIVE_PLATFORM_H */ diff --git a/fpga/src/main/resources/vc709/uartboot/include/riscv_test_defaults.h b/fpga/src/main/resources/vc709/uartboot/include/riscv_test_defaults.h new file mode 100644 index 0000000000..a2dea3d4ad --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/include/riscv_test_defaults.h @@ -0,0 +1,81 @@ +// See LICENSE for license details. +#ifndef _RISCV_TEST_DEFAULTS_H +#define _RISCV_TEST_DEFAULTS_H + +#define TESTNUM x28 +#define TESTBASE 0x4000 + +#define RVTEST_RV32U \ + .macro init; \ + .endm + +#define RVTEST_RV64U \ + .macro init; \ + .endm + +#define RVTEST_RV32UF \ + .macro init; \ + /* If FPU exists, initialize FCSR. */ \ + csrr t0, misa; \ + andi t0, t0, 1 << ('F' - 'A'); \ + beqz t0, 1f; \ + /* Enable FPU if it exists. */ \ + li t0, MSTATUS_FS; \ + csrs mstatus, t0; \ + fssr x0; \ +1: ; \ + .endm + +#define RVTEST_RV64UF \ + .macro init; \ + /* If FPU exists, initialize FCSR. */ \ + csrr t0, misa; \ + andi t0, t0, 1 << ('F' - 'A'); \ + beqz t0, 1f; \ + /* Enable FPU if it exists. */ \ + li t0, MSTATUS_FS; \ + csrs mstatus, t0; \ + fssr x0; \ +1: ; \ + .endm + +#define RVTEST_CODE_BEGIN \ + .section .text.init; \ + .globl _prog_start; \ +_prog_start: \ + init; + +#define RVTEST_CODE_END \ + unimp + +#define RVTEST_PASS \ + fence; \ + li t0, TESTBASE; \ + li t1, 0x5555; \ + sw t1, 0(t0); \ +1: \ + j 1b; + +#define RVTEST_FAIL \ + li t0, TESTBASE; \ + li t1, 0x3333; \ + slli a0, a0, 16; \ + add a0, a0, t1; \ + sw a0, 0(t0); \ +1: \ + j 1b; + +#define EXTRA_DATA + +#define RVTEST_DATA_BEGIN \ + EXTRA_DATA \ + .align 4; .global begin_signature; begin_signature: + +#define RVTEST_DATA_END \ + _msg_init: .asciz "RUN\r\n"; \ + _msg_pass: .asciz "PASS"; \ + _msg_fail: .asciz "FAIL "; \ + _msg_end: .asciz "\r\n"; \ + .align 4; .global end_signature; end_signature: + +#endif /* _RISCV_TEST_DEFAULTS_H */ diff --git a/fpga/src/main/resources/vc709/uartboot/include/sections.h b/fpga/src/main/resources/vc709/uartboot/include/sections.h new file mode 100644 index 0000000000..6e1f0518bc --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/include/sections.h @@ -0,0 +1,17 @@ +// See LICENSE for license details. +#ifndef _SECTIONS_H +#define _SECTIONS_H + +extern unsigned char _rom[]; +extern unsigned char _rom_end[]; + +extern unsigned char _ram[]; +extern unsigned char _ram_end[]; + +extern unsigned char _ftext[]; +extern unsigned char _etext[]; +extern unsigned char _fbss[]; +extern unsigned char _ebss[]; +extern unsigned char _end[]; + +#endif /* _SECTIONS_H */ diff --git a/fpga/src/main/resources/vc709/uartboot/include/serial.h b/fpga/src/main/resources/vc709/uartboot/include/serial.h new file mode 100644 index 0000000000..aa2788299f --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/include/serial.h @@ -0,0 +1,23 @@ +#ifndef __SERIAL_BOOT_ +#define __SERIAL_BOOT_ + +#include +#include "platform.h" +#include "crc16.h" +#include "kprintf.h" + +#define MAX_CORES 4 + +typedef enum _cmd_t +{ + UART_CMD_TRANSFER, + UART_CMD_END +} cmd_t; + +typedef struct _package_t +{ + uint8_t *addr; + long len; +} package_t; + +#endif \ No newline at end of file diff --git a/fpga/src/main/resources/vc709/uartboot/include/smp.h b/fpga/src/main/resources/vc709/uartboot/include/smp.h new file mode 100644 index 0000000000..145ceb37f4 --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/include/smp.h @@ -0,0 +1,142 @@ +#ifndef SIFIVE_SMP +#define SIFIVE_SMP +#include "platform.h" + +// The maximum number of HARTs this code supports +#ifndef MAX_HARTS +#define MAX_HARTS 32 +#endif +#define CLINT_END_HART_IPI CLINT_CTRL_ADDR + (MAX_HARTS*4) +#define CLINT1_END_HART_IPI CLINT1_CTRL_ADDR + (MAX_HARTS*4) + +// The hart that non-SMP tests should run on +#ifndef NONSMP_HART +#define NONSMP_HART 0 +#endif + +/* If your test cannot handle multiple-threads, use this: + * smp_disable(reg1) + */ +#define smp_disable(reg1, reg2) \ + csrr reg1, mhartid ;\ + li reg2, NONSMP_HART ;\ + beq reg1, reg2, hart0_entry ;\ +42: ;\ + wfi ;\ + j 42b ;\ +hart0_entry: + +/* If your test needs to temporarily block multiple-threads, do this: + * smp_pause(reg1, reg2) + * ... single-threaded work ... + * smp_resume(reg1, reg2) + * ... multi-threaded work ... + */ + +#define smp_pause(reg1, reg2) \ + li reg2, 0x8 ;\ + csrw mie, reg2 ;\ + li reg1, NONSMP_HART ;\ + csrr reg2, mhartid ;\ + bne reg1, reg2, 42f + +#ifdef CLINT1_CTRL_ADDR +// If a second CLINT exists, then make sure we: +// 1) Trigger a software interrupt on all harts of both CLINTs. +// 2) Locate your own hart's software interrupt pending register and clear it. +// 3) Wait for all harts on both CLINTs to clear their software interrupt +// pending register. +// WARNING: This code makes these assumptions, which are only true for Fadu as +// of now: +// 1) hart0 uses CLINT0 at offset 0 +// 2) hart2 uses CLINT1 at offset 0 +// 3) hart3 uses CLINT1 at offset 1 +// 4) There are no other harts or CLINTs in the system. +#define smp_resume(reg1, reg2) \ + /* Trigger software interrupt on CLINT0 */ \ + li reg1, CLINT_CTRL_ADDR ;\ +41: ;\ + li reg2, 1 ;\ + sw reg2, 0(reg1) ;\ + addi reg1, reg1, 4 ;\ + li reg2, CLINT_END_HART_IPI ;\ + blt reg1, reg2, 41b ;\ + /* Trigger software interrupt on CLINT1 */ \ + li reg1, CLINT1_CTRL_ADDR ;\ +41: ;\ + li reg2, 1 ;\ + sw reg2, 0(reg1) ;\ + addi reg1, reg1, 4 ;\ + li reg2, CLINT1_END_HART_IPI ;\ + blt reg1, reg2, 41b ;\ + /* Wait to receive software interrupt */ \ +42: ;\ + wfi ;\ + csrr reg2, mip ;\ + andi reg2, reg2, 0x8 ;\ + beqz reg2, 42b ;\ + /* Clear own software interrupt bit */ \ + csrr reg2, mhartid ;\ + bnez reg2, 41f; \ + /* hart0 case: Use CLINT0 */ \ + li reg1, CLINT_CTRL_ADDR ;\ + slli reg2, reg2, 2 ;\ + add reg2, reg2, reg1 ;\ + sw zero, 0(reg2) ;\ + j 42f; \ +41: \ + /* hart 2, 3 case: Use CLINT1 and remap hart IDs to 0 and 1 */ \ + li reg1, CLINT1_CTRL_ADDR ;\ + addi reg2, reg2, -2; \ + slli reg2, reg2, 2 ;\ + add reg2, reg2, reg1 ;\ + sw zero, 0(reg2) ; \ +42: \ + /* Wait for all software interrupt bits to be cleared on CLINT0 */ \ + li reg1, CLINT_CTRL_ADDR ;\ +41: ;\ + lw reg2, 0(reg1) ;\ + bnez reg2, 41b ;\ + addi reg1, reg1, 4 ;\ + li reg2, CLINT_END_HART_IPI ;\ + blt reg1, reg2, 41b; \ + /* Wait for all software interrupt bits to be cleared on CLINT1 */ \ + li reg1, CLINT1_CTRL_ADDR ;\ +41: ;\ + lw reg2, 0(reg1) ;\ + bnez reg2, 41b ;\ + addi reg1, reg1, 4 ;\ + li reg2, CLINT1_END_HART_IPI ;\ + blt reg1, reg2, 41b; \ + /* End smp_resume() */ + +#else + +#define smp_resume(reg1, reg2) \ + li reg1, CLINT_CTRL_ADDR ;\ +41: ;\ + li reg2, 1 ;\ + sw reg2, 0(reg1) ;\ + addi reg1, reg1, 4 ;\ + li reg2, CLINT_END_HART_IPI ;\ + blt reg1, reg2, 41b ;\ +42: ;\ + wfi ;\ + csrr reg2, mip ;\ + andi reg2, reg2, 0x8 ;\ + beqz reg2, 42b ;\ + li reg1, CLINT_CTRL_ADDR ;\ + csrr reg2, mhartid ;\ + slli reg2, reg2, 2 ;\ + add reg2, reg2, reg1 ;\ + sw zero, 0(reg2) ;\ +41: ;\ + lw reg2, 0(reg1) ;\ + bnez reg2, 41b ;\ + addi reg1, reg1, 4 ;\ + li reg2, CLINT_END_HART_IPI ;\ + blt reg1, reg2, 41b + +#endif /* ifdef CLINT1_CTRL_ADDR */ + +#endif diff --git a/fpga/src/main/resources/vc709/uartboot/kprintf.c b/fpga/src/main/resources/vc709/uartboot/kprintf.c new file mode 100644 index 0000000000..9dec7a7a04 --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/kprintf.c @@ -0,0 +1,145 @@ +// See LICENSE for license details. +#include +#include +#include + +#include "kprintf.h" + +static inline void _kputs(const char *s) +{ + char c; + for (; (c = *s) != '\0'; s++) + kputc(c); +} + +static inline void _kgets(char *s) +{ + for (; (*s = kgetc()) != '\n'; s++); + *s = '\0'; +} + +void kread(char *s, int count) +{ + char *p = s; + for (int i = 0; i < count; i++) { + *p++ = kgetc(); + } +} + +void kwrite(char *s, int count) +{ + char *p = s; + for (int i = 0; i < count; i++) { + kputc(*p++); + } +} + +void kputs(const char *s) +{ + _kputs(s); + kputc('\n'); +} + +void kgets(char *s){ + _kgets(s); +} + +void _escape_char(const char c){ + switch (c) { + case 'a': + kputc('\n'); + break; + case 'b': + kputc('\n'); + break; + case 'f': + kputc('\n'); + break; + case 'n': + kputc('\n'); + break; + case 'r': + kputc('\n'); + break; + case 't': + kputc('\n'); + break; + case 'v': + kputc('\n'); + break; + case '\\': + kputc('\n'); + break; + case '\'': + kputc('\''); + break; + case '"': + kputc('"'); + break; + case '?': + kputc('?'); + break; + case '0': + kputc('\0'); + break; + default: + break; + } +} + +void kprintf(const char *fmt, ...) +{ + va_list vl; + bool is_format, is_long, is_char; + char c; + + va_start(vl, fmt); + is_format = false; + is_long = false; + is_char = false; + while ((c = *fmt++) != '\0') { + if (is_format) { + switch (c) { + case 'l': + is_long = true; + continue; + case 'h': + is_char = true; + continue; + case 'x': { + unsigned long n; + long i; + if (is_long) { + n = va_arg(vl, unsigned long); + i = (sizeof(unsigned long) << 3) - 4; + } else { + n = va_arg(vl, unsigned int); + i = is_char ? 4 : (sizeof(unsigned int) << 3) - 4; + } + for (; i >= 0; i -= 4) { + long d; + d = (n >> i) & 0xF; + kputc(d < 10 ? '0' + d : 'a' + d - 10); + } + break; + } + case 's': + _kputs(va_arg(vl, const char *)); + break; + case 'c': + kputc(va_arg(vl, int)); + break; + } + is_format = false; + is_long = false; + is_char = false; + } else if (c == '%') { + is_format = true; + } else if (c == '\\') { + _escape_char(*(fmt++)); + } else { + kputc(c); + } + } + va_end(vl); +} diff --git a/fpga/src/main/resources/vc709/uartboot/linker/bootrom.elf.lds b/fpga/src/main/resources/vc709/uartboot/linker/bootrom.elf.lds new file mode 100644 index 0000000000..6843436fae --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/linker/bootrom.elf.lds @@ -0,0 +1,80 @@ +OUTPUT_ARCH("riscv") +ENTRY(_prog_start) + +INCLUDE memory.lds + +PHDRS +{ + text PT_LOAD; + data PT_LOAD; + bss PT_LOAD; +} + +SECTIONS +{ + PROVIDE(_ram = ORIGIN(memory_mem)); + PROVIDE(_ram_end = _ram + LENGTH(memory_mem)); + + .text ALIGN((ORIGIN(bootrom_mem) + 0x0), 8) : AT(ALIGN((ORIGIN(bootrom_mem) + 0x0), 8)) { + PROVIDE(_ftext = .); + *(.text.init) + *(.text.unlikely .text.unlikely.*) + *(.text .text.* .gnu.linkonce.t.*) + PROVIDE(_etext = .); + . += 0x40; /* to create a gap between .text and .data b/c ifetch can fetch ahead from .data */ + } >bootrom_mem :text + + .eh_frame ALIGN((ADDR(.text) + SIZEOF(.text)), 8) : AT(ALIGN((LOADADDR(.text) + SIZEOF(.text)), 8)) { + *(.eh_frame) + } >bootrom_mem :text + + .srodata ALIGN((ADDR(.eh_frame) + SIZEOF(.eh_frame)), 8) : AT(ALIGN((LOADADDR(.eh_frame) + SIZEOF(.eh_frame)), 8)) ALIGN_WITH_INPUT { + *(.srodata.cst16) + *(.srodata.cst8) + *(.srodata.cst4) + *(.srodata.cst2) + *(.srodata.*) + } >bootrom_mem :data + + .data ALIGN((ADDR(.srodata) + SIZEOF(.srodata)), 8) : AT(ALIGN((LOADADDR(.srodata) + SIZEOF(.srodata)), 8)) ALIGN_WITH_INPUT { + *(.data .data.* .gnu.linkonce.d.*) + *(.tohost) /* TODO: Support sections that aren't explicitly listed in this linker script */ + } >bootrom_mem :data + + .sdata ALIGN((ADDR(.data) + SIZEOF(.data)), 8) : AT(ALIGN((LOADADDR(.data) + SIZEOF(.data)), 8)) ALIGN_WITH_INPUT { + *(.sdata .sdata.* .gnu.linkonce.s.*) + } >bootrom_mem :data + + .rodata ALIGN((ADDR(.sdata) + SIZEOF(.sdata)), 8) : AT(ALIGN((LOADADDR(.sdata) + SIZEOF(.sdata)), 8)) ALIGN_WITH_INPUT { + *(.rodata .rodata.* .gnu.linkonce.r.*) + *(.dtb) /* Must be last if this code is added to RC's BootROM */ + } >bootrom_mem :data + + PROVIDE(_data = ADDR(.rodata)); + PROVIDE(_data_lma = LOADADDR(.rodata)); + PROVIDE(_edata = .); + + .bss ALIGN((ORIGIN(memory_mem) + 0x0), 8) : AT(ALIGN((ORIGIN(memory_mem) + 0x0), 8)) ALIGN(8) { + PROVIDE(_fbss = .); + PROVIDE(__global_pointer$ = . + 0x7C0); + *(.sbss .sbss.* .gnu.linkonce.sb.*) + *(.bss .bss.* .gnu.linkonce.b.*) + . = ALIGN(8); + PROVIDE(_ebss = .); + } >memory_mem :bss + + PROVIDE(_end = .); + + /* + * heap_stack_region_usable_end: (ORIGIN(memory_mem) + LENGTH(memory_mem)) + * heap_stack_min_size: 4096 + * heap_stack_max_size: 1048576 + */ + PROVIDE(_sp = ALIGN(MIN((ORIGIN(memory_mem) + LENGTH(memory_mem)), _ebss + 1048576) - 7, 8)); + PROVIDE(_heap_end = _sp - 2048); + + /* This section is a noop and is only used for the ASSERT */ + .stack : { + ASSERT(_sp >= (_ebss + 4096), "Error: No room left for the heap and stack"); + } +} diff --git a/fpga/src/main/resources/vc709/uartboot/linker/memory.lds b/fpga/src/main/resources/vc709/uartboot/linker/memory.lds new file mode 100644 index 0000000000..65737bb3c0 --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/linker/memory.lds @@ -0,0 +1,5 @@ +MEMORY +{ + bootrom_mem (rx) : ORIGIN = 0x10000, LENGTH = 0x2000 + memory_mem (rwx) : ORIGIN = 0x80000000, LENGTH = 0x40000000 +} \ No newline at end of file diff --git a/fpga/src/main/resources/vc709/uartboot/plic.c b/fpga/src/main/resources/vc709/uartboot/plic.c new file mode 100644 index 0000000000..91156eb1a1 --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/plic.c @@ -0,0 +1,3 @@ +#include + + diff --git a/fpga/src/main/resources/vc709/uartboot/sd.c b/fpga/src/main/resources/vc709/uartboot/sd.c new file mode 100644 index 0000000000..0e882572ca --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/sd.c @@ -0,0 +1,221 @@ +// See LICENSE for license details. +#include + +#include "include/platform.h" +#include "common.h" +#include "crc16.h" + +#define DEBUG +#include "kprintf.h" + +#define MAX_CORES 8 + +#define PAYLOAD_SIZE (26 << 11) + +#ifndef TL_CLK +#error Must define TL_CLK +#endif + +#define F_CLK TL_CLK + +static volatile uint32_t * const spi = (void *)(SPI_CTRL_ADDR); + +static inline uint8_t spi_xfer(uint8_t d) +{ + int32_t r; + + REG32(spi, SPI_REG_TXFIFO) = d; + do { + r = REG32(spi, SPI_REG_RXFIFO); + } while (r < 0); + return r; +} + +static inline uint8_t sd_dummy(void) +{ + return spi_xfer(0xFF); +} + +static uint8_t sd_cmd(uint8_t cmd, uint32_t arg, uint8_t crc) +{ + unsigned long n; + uint8_t r; + + REG32(spi, SPI_REG_CSMODE) = SPI_CSMODE_HOLD; + sd_dummy(); + spi_xfer(cmd); + spi_xfer(arg >> 24); + spi_xfer(arg >> 16); + spi_xfer(arg >> 8); + spi_xfer(arg); + spi_xfer(crc); + + n = 1000; + do { + r = sd_dummy(); + if (!(r & 0x80)) { + dprintf("sd:cmd: %hx\r\n", r); + goto done; + } + } while (--n > 0); + kputs("sd_cmd: timeout"); +done: + return r; +} + +static inline void sd_cmd_end(void) +{ + sd_dummy(); + REG32(spi, SPI_REG_CSMODE) = SPI_CSMODE_AUTO; +} + + +static void sd_poweron(void) +{ + long i; + REG32(spi, SPI_REG_SCKDIV) = (F_CLK / 300000UL); + REG32(spi, SPI_REG_CSMODE) = SPI_CSMODE_OFF; + for (i = 10; i > 0; i--) { + sd_dummy(); + } + REG32(spi, SPI_REG_CSMODE) = SPI_CSMODE_AUTO; +} + +static int sd_cmd0(void) +{ + int rc; + dputs("CMD0"); + rc = (sd_cmd(0x40, 0, 0x95) != 0x01); + sd_cmd_end(); + return rc; +} + +static int sd_cmd8(void) +{ + int rc; + dputs("CMD8"); + rc = (sd_cmd(0x48, 0x000001AA, 0x87) != 0x01); + sd_dummy(); /* command version; reserved */ + sd_dummy(); /* reserved */ + rc |= ((sd_dummy() & 0xF) != 0x1); /* voltage */ + rc |= (sd_dummy() != 0xAA); /* check pattern */ + sd_cmd_end(); + return rc; +} + +static void sd_cmd55(void) +{ + sd_cmd(0x77, 0, 0x65); + sd_cmd_end(); +} + +static int sd_acmd41(void) +{ + uint8_t r; + dputs("ACMD41"); + do { + sd_cmd55(); + r = sd_cmd(0x69, 0x40000000, 0x77); /* HCS = 1 */ + } while (r == 0x01); + return (r != 0x00); +} + +static int sd_cmd58(void) +{ + int rc; + dputs("CMD58"); + rc = (sd_cmd(0x7A, 0, 0xFD) != 0x00); + rc |= ((sd_dummy() & 0x80) != 0x80); /* Power up status */ + sd_dummy(); + sd_dummy(); + sd_dummy(); + sd_cmd_end(); + return rc; +} + +static int sd_cmd16(void) +{ + int rc; + dputs("CMD16"); + rc = (sd_cmd(0x50, 0x200, 0x15) != 0x00); + sd_cmd_end(); + return rc; +} + +#define SPIN_SHIFT 6 +#define SPIN_UPDATE(i) (!((i) & ((1 << SPIN_SHIFT)-1))) +#define SPIN_INDEX(i) (((i) >> SPIN_SHIFT) & 0x3) + +static const char spinner[] = { '-', '/', '|', '\\' }; + +static int copy(void) +{ + volatile uint8_t *p = (void *)(PAYLOAD_DEST); + long i = PAYLOAD_SIZE; + int rc = 0; + + dputs("CMD18"); + kprintf("LOADING"); + + REG32(spi, SPI_REG_SCKDIV) = (F_CLK / 16666666UL); + if (sd_cmd(0x52, 0, 0xE1) != 0x00) { + sd_cmd_end(); + return 1; + } + do { + volatile uint8_t *q = p; + + // transfer data + while (sd_dummy() != 0xFE); + for (int i = 0; i < 512; i++) { + uint8_t x = sd_dummy(); + *p++ = x; + } + + // crc16 512-bytes + crc = crc16(q); + + // expected crc16 + uint16_t crc_exp = ((uint16_t)sd_dummy() << 8); + crc_exp |= sd_dummy(); + + if (crc != crc_exp) { + kputs("\b- CRC mismatch "); + rc = 1; + break; + } + + if (SPIN_UPDATE(i)) { + kputc('\b'); + kputc(spinner[SPIN_INDEX(i)]); + } + } while (--i > 0); + sd_cmd_end(); + + sd_cmd(0x4C, 0, 0x01); + sd_cmd_end(); + kputs("\b "); + return rc; +} + +int main(void) +{ + REG32(uart, UART_REG_TXCTRL) = UART_TXEN; + + kputs("INIT"); + sd_poweron(); + if (sd_cmd0() || + sd_cmd8() || + sd_acmd41() || + sd_cmd58() || + sd_cmd16() || + copy()) { + kputs("ERROR"); + return 1; + } + + kputs("BOOT"); + + __asm__ __volatile__ ("fence.i" : : : "memory"); + return 0; +} diff --git a/fpga/src/main/resources/vc709/uartboot/serial.c b/fpga/src/main/resources/vc709/uartboot/serial.c new file mode 100644 index 0000000000..0752b886f6 --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/serial.c @@ -0,0 +1,64 @@ +#include "./include/serial.h" + +static int read_block(uint8_t *q) +{ + int retry = -1; + char cmd = NAK; + + uint16_t crc_exp; + do { + retry++; + uint8_t *p = q; + // receive file and crc + kread((char *)p, CRC16_LEN); + kread((char *)&crc_exp, 2); + // ACK/NAK + cmd = ((crc16(p) == crc_exp) ? ACK : NAK); + kwrite(&cmd, 1); + } while (cmd != ACK); + + return retry; +} + +static int read(uint8_t *addr, long len) +{ + uint8_t *p = addr; + int retry = 0; + int n_blocks = len >> CRC16_BITS; + n_blocks += (((len % CRC16_LEN) == 0)? 0 : 1); + for (int i = 0; i < n_blocks; i++, p += CRC16_LEN) { + retry += read_block(p); + } + return retry; +} + +static void session(void) +{ + cmd_t cmd; + while (cmd != UART_CMD_END) { + kread((char *)&cmd, sizeof(cmd_t)); + if (cmd == UART_CMD_TRANSFER) { + package_t package; + kread((char*)&package, sizeof(package_t)); + read(package.addr, package.len); + } + } +} + +int main(void) +{ + REG32(uart, UART_REG_TXCTRL) = UART_TXEN; + REG32(uart, UART_REG_RXCTRL) = UART_RXEN; + + kputs("BOOT INIT"); + + session(); + + kputs("BOOT END"); + + REG32(uart, UART_REG_TXCTRL) &= ~UART_TXEN; + REG32(uart, UART_REG_RXCTRL) &= ~UART_RXEN; + + __asm__ __volatile__ ("fence.i" : : : "memory"); + return 0; +} diff --git a/fpga/src/main/resources/vc709/uartboot/uart.c b/fpga/src/main/resources/vc709/uartboot/uart.c new file mode 100644 index 0000000000..a6f505f69f --- /dev/null +++ b/fpga/src/main/resources/vc709/uartboot/uart.c @@ -0,0 +1,50 @@ +// See LICENSE for license details. +#include + +#include "./include/platform.h" +#include "common.h" + +#define DEBUG + +#include "kprintf.h" + +#define is_num(c) ((c>='0')&&(c<='9')) +#define is_lower(c) ((c>='a')&&(c<='z')) +#define is_upper(c) ((c>='A')&&(c<='Z')) +#define is_alpha(c) (is_lower(c)||is_upper(c)) + +static int strcmp(const char *p, const char *q) { + // equal + for( ; *p == *q; ++p, ++q) + if(*p == '\0') + return (0); + // not equal + return (*(unsigned char *)p < *(unsigned char *)q) ? -1 : +1; +} + +static int stoi(char *p) { + int val = 0; + if (*p == '0' && *(p+1) == 'x') { + p += 2; + for ( ; *p != '\0'; p++) { + if (is_num(*p)) { + val = (val << 4) + (*p - '0'); + } else if (is_lower(*p)) { + val = (val << 4) + (*p - 'a' + 10); + } else if (is_upper(*p)) { + val = (val << 4) + (*p - 'A' + 10); + } else { + break; + } + } + } else { + for ( ; *p != '\0'; p++) { + if (is_num(*p)) { + val = val * 10 + (*p - '0'); + } else { + break; + } + } + } + return val; +} \ No newline at end of file diff --git a/fpga/src/main/resources/vc709/uartsend/Makefile b/fpga/src/main/resources/vc709/uartsend/Makefile new file mode 100644 index 0000000000..a598850707 --- /dev/null +++ b/fpga/src/main/resources/vc709/uartsend/Makefile @@ -0,0 +1,9 @@ +build: + CC=gcc + $(CC) -c ../uartboot/crc16.c -o ../uartboot/crc16.o + $(CC) -c serial.c -o serial.o + $(CC) -o serial serial.o ../uartboot/crc16.o + +clean: + rm serial.o ../uartboot/crc16.o + rm serial diff --git a/fpga/src/main/resources/vc709/uartsend/serial.c b/fpga/src/main/resources/vc709/uartsend/serial.c new file mode 100644 index 0000000000..58607101ad --- /dev/null +++ b/fpga/src/main/resources/vc709/uartsend/serial.c @@ -0,0 +1,285 @@ +#include +#include +#include +#include +#include +#include //文件控制定义 +#include //终端控制定义 +#include +#include +#include +#include +#include + +#include "../uartboot/include/serial.h" + +#define DEVICE "/dev/ttyUSB0" +#define S_TIMEOUT 1 + +int serial_fd = 0; + +//打开串口并初始化设置 +int init_serial(char *device) +{ + serial_fd = open(device, O_RDWR | O_NOCTTY | O_NDELAY); + if (serial_fd < 0) { + perror("open"); + return -1; + } + + //串口主要设置结构体termios + struct termios options; + + /**1. tcgetattr函数用于获取与终端相关的参数。 + *参数fd为终端的文件描述符,返回的结果保存在termios结构体中 + */ + + tcgetattr(serial_fd, &options); + /**2. 修改所获得的参数*/ + options.c_cflag |= (CLOCAL | CREAD); //设置控制模式状态,本地连接,接收使能 + options.c_cflag &= ~CSIZE; //字符长度,设置数据位之前一定要屏掉这个位 + options.c_cflag &= ~CRTSCTS; //无硬件流控 + options.c_cflag |= CS8; //8位数据长度 + options.c_cflag &= ~CSTOPB; //1位停止位 + options.c_iflag |= IGNPAR; //无奇偶检验位 + options.c_oflag = 0; //输出模式 + options.c_lflag = 0; //不激活终端模式 + cfsetospeed(&options, B115200); //设置波特率 + + /**3. 设置新属性,TCSANOW:所有改变立即生效*/ + tcflush(serial_fd, TCIFLUSH); //溢出数据可以接收,但不读 + tcsetattr(serial_fd, TCSANOW, &options); + + return 0; +} + +/** +*串口发送数据 +*@fd:串口描述符 +*@data:待发送数据 +*@datalen:数据长度 +*/ +unsigned int total_send = 0 ; +int uart_send(int fd, uint8_t *data, int datalen) +{ + int len = 0; + len = write(fd, data, datalen);//实际写入的长度 + if(len == datalen) { + total_send += len; + return len; + } else { + tcflush(fd, TCOFLUSH);//TCOFLUSH刷新写入的数据但不传送 + return -1; + } + return 0; +} + +/** +*串口接收数据 +*要求启动后,在pc端发送ascii文件 +*/ +unsigned int total_length = 0 ; +int uart_recv(int fd, uint8_t *data, int datalen) +{ + int len=0, ret = 0; + fd_set fs_read; + struct timeval tv_timeout; + + FD_ZERO(&fs_read); + FD_SET(fd, &fs_read); + +#ifdef S_TIMEOUT + tv_timeout.tv_sec = (10*20/115200+2); + tv_timeout.tv_usec = 0; + ret = select(fd+1, &fs_read, NULL, NULL, NULL); +#elif + ret = select(fd+1, &fs_read, NULL, NULL, tv_timeout); +#endif + + //如果返回0,代表在描述符状态改变前已超过timeout时间,错误返回-1 + + if (FD_ISSET(fd, &fs_read)) { + len = read(fd, data, datalen); + total_length += len ; + return len; + } else { + perror("select"); + return -1; + } + + return 0; +} + +void readline(char *p) +{ + char *q = p; + do { + uart_recv(serial_fd, (uint8_t *)q, sizeof(uint8_t)); + } while (*q++ != '\n'); + *q = '\0'; +} + +void write_cmd(cmd_t cmd) +{ + uart_send(serial_fd, (uint8_t *)&cmd, sizeof(cmd)); +} + +size_t write_block(int serial_fd, char *buf) +{ + size_t retry = -1; + char cmd = NAK; + + // calculate crc + uint16_t crc_exp = crc16((uint8_t *)buf); + do { + retry++; + // send file and crc + uart_send(serial_fd, (uint8_t *)buf, CRC16_LEN); + uart_send(serial_fd, (uint8_t *)&crc_exp, 2); + // ACK/NAK + uart_recv(serial_fd, (uint8_t *)&cmd, sizeof(char)); + } while (cmd != ACK); + + return retry; +} + +char bar[101]; + +void update_progress(char *bar, uint8_t p){ + + bar[p] = '#'; + bar[p + 1] = '\0'; + + printf("send blocks: [%-100s][%3d%%]\r", bar, p); +} + +size_t write_batch(int serial_id, char *buf, size_t num_blocks){ + + size_t retry = 0; + + for (size_t i = 0; i < num_blocks; i++) { + retry += write_block(serial_fd, buf + i * CRC16_LEN); + update_progress(bar, i * 100 / num_blocks); + } + + printf("\n"); + + return retry; +} + +void write_header(uint8_t *addr, long len) +{ + // send metadata + package_t package; + package.addr = addr; + package.len = len; + + uart_send(serial_fd, (uint8_t *)&package, sizeof(package)); +} + +void write_file(FILE *fd) +{ + // send file + size_t n_blocks = 0; + size_t n_bytes = 0; + size_t size; + size_t retry = 0; + + char *buf = (char *)malloc(sizeof(char) * CRC16_LEN * NUM_BLOCKS); + + do { + size = fread(buf, CRC16_LEN, NUM_BLOCKS, fd); + if (size == -1) { + perror("read"); + exit(1); + } + if (size > 0) { + retry += write_batch(serial_fd, buf, size); + n_blocks += size; + n_bytes += size * CRC16_LEN; + printf("send %5ld blocks, retry %5ld times.\n", size, retry); + } + } while (size != 0); + + if (size == 0) { + memset(buf, 0, CRC16_LEN * NUM_BLOCKS); + fseek(fd, CRC16_LEN * n_blocks, SEEK_SET); + size = fread(buf, sizeof(char), CRC16_LEN, fd); + if (size == -1) { + perror("read"); + exit(1); + } + if (size > 0) { + retry += write_block(serial_fd, buf); + n_blocks += 1; + n_bytes += size; + printf("send %ld bytes, retry %ld times.\n", size, retry); + } + } + free(buf); + + printf("send %ld blocks, %ld bytes, retry %ld times in total\n", n_blocks, n_bytes, retry); +} + +int send_file(char *address, char *filename) +{ + FILE *fd = fopen(filename, "r"); + + if (fd == NULL) { + printf("open file failed.\n"); + perror("open"); + return -1; + } + + printf("open file successfully.\n"); + + // get file len + long len; + fseek(fd, 0L, SEEK_END); + len = ftell(fd); + fseek(fd, 0L, SEEK_SET); + + printf("file len: %ld\n", len); + + // parse address + uint8_t *addr; + sscanf(address, "%p", &addr); + printf("start transfer at addr[%p].\n", addr); + + // send cmd, header and file + write_cmd(UART_CMD_TRANSFER); + write_header(addr, len); + write_file(fd); + + fclose(fd); + + return 0; +} + +char msg[256]; + +int main(int argc, char *argv[]) +{ + for (int i = 0; i < argc; i++) + printf("argv[%d]: %s\n", i, argv[i]); + + // init connection + if (init_serial(argv[1]) != 0) { + printf("open serial failed.\n"); + exit(-1); + } + printf("open serial successfully.\n"); + + if (argc == 4) { + // ./serial tty, address, filename + send_file(argv[2], argv[3]); + printf("transfer finished.\n"); + } else { + // ./serial tty + write_cmd(UART_CMD_END); + } + + close(serial_fd); + + return 0; +} diff --git a/fpga/src/main/scala/vc709/Configs.scala b/fpga/src/main/scala/vc709/Configs.scala new file mode 100644 index 0000000000..441f40fe9c --- /dev/null +++ b/fpga/src/main/scala/vc709/Configs.scala @@ -0,0 +1,78 @@ +package chipyard.fpga.vc709 + +import sys.process._ + +import freechips.rocketchip.config.{Config, Parameters} +import freechips.rocketchip.subsystem.{SystemBusKey, PeripheryBusKey, ControlBusKey, ExtMem} +import freechips.rocketchip.devices.tilelink.{BootROMLocated} +import freechips.rocketchip.diplomacy.{DTSModel, DTSTimebase, RegionType, AddressSet} +import freechips.rocketchip.tile.{XLen} + +import sifive.blocks.devices.i2c.{PeripheryI2CKey, I2CParams} +import sifive.blocks.devices.uart.{PeripheryUARTKey, UARTParams} +import sifive.blocks.devices.gpio.{PeripheryGPIOKey, GPIOParams} + +import sifive.fpgashells.shell.{DesignKey} +import sifive.fpgashells.shell.xilinx.{VC709DDR3Size} +import testchipip.{SerialTLKey} + +import chipyard.{BuildTop, BuildSystem, ExtTLMem} +import chipyard.fpga.vcu118.{WithUARTIOPassthrough, WithTLIOPassthrough, WithFPGAFrequency} +import chipyard.fpga.vcu118.bringup.{WithI2CIOPassthrough, WithGPIOIOPassthrough} + +class WithDefaultPeripherals extends Config((site, here, up) => { + case PeripheryUARTKey => List(UARTParams(address = BigInt(0x64000000L), nTxEntries = 256, nRxEntries = 256)) + case PeripheryGPIOKey => List(GPIOParams(address = BigInt(0x64002000L), width = 21)) + case PeripheryI2CKey => List(I2CParams(address = BigInt(0x64005000L))) +}) + +class WithSystemModifications extends Config((site, here, up) => { + case PeripheryBusKey => up(PeripheryBusKey, site).copy(dtsFrequency = Some(site(FPGAFrequencyKey).toInt*1000000)) + case DTSTimebase => BigInt(1000000) + case BootROMLocated(x) => up(BootROMLocated(x), site).map { p => + // invoke makefile for uart boot + val freqMHz = site(FPGAFrequencyKey).toInt * 1000000 + val make = s"make -C fpga/src/main/resources/vc709/uartboot PBUS_CLK=${freqMHz} bin" + require (make.! == 0, "Failed to build bootrom") + p.copy(hang = 0x10000, contentFileName = s"./fpga/src/main/resources/vc709/uartboot/build/bootrom.bin") + } + case ExtMem => up(ExtMem, site).map(x => x.copy(master = x.master.copy(size = site(VC709DDR3Size)))) // set extmem to DDR size + case SerialTLKey => None // remove serialized tl port +}) + +// DOC include start: AbstractVC709 and Rocket +class WithVC709Tweaks extends Config( + new WithUART ++ + new WithI2C ++ + new WithGPIO ++ + new WithDDRMem ++ + new WithUARTIOPassthrough ++ + new WithI2CIOPassthrough ++ + new WithGPIOIOPassthrough ++ + new WithTLIOPassthrough ++ + new WithDefaultPeripherals ++ + new chipyard.config.WithTLBackingMemory ++ // use TL backing memory + new WithSystemModifications ++ // setup busses, use uart bootrom, setup ext. mem. size + new chipyard.config.WithNoDebug ++ // remove debug module + new freechips.rocketchip.subsystem.WithoutTLMonitors ++ + new freechips.rocketchip.subsystem.WithNMemoryChannels(1)) + +class WithVC709System extends Config((site, here, up) => { + case BuildSystem => (p: Parameters) => new VC709DigitalTop()(p) +}) + +class RocketVC709Config extends Config( + new WithVC709System ++ + new WithVC709Tweaks ++ + new chipyard.RocketConfig) +// DOC include end: AbstractVC709 and Rocket + +class SmallLargeBoomConfig extends Config( + new boom.common.WithNSmallBooms(4) ++ // 4 boom cores + new chipyard.config.AbstractConfig) + +class BoomVC709Config extends Config( + new WithFPGAFrequency(50) ++ + new WithVC709System ++ + new WithVC709Tweaks ++ + new SmallLargeBoomConfig) diff --git a/fpga/src/main/scala/vc709/CustomOverlays.scala b/fpga/src/main/scala/vc709/CustomOverlays.scala new file mode 100644 index 0000000000..5081310ad1 --- /dev/null +++ b/fpga/src/main/scala/vc709/CustomOverlays.scala @@ -0,0 +1,98 @@ +package chipyard.fpga.vc709 + +import chisel3._ + +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.config.{Parameters, Field} +import freechips.rocketchip.tilelink.{TLInwardNode, TLAsyncCrossingSink} + +import sifive.fpgashells.shell._ +import sifive.fpgashells.ip.xilinx._ +import sifive.fpgashells.shell.xilinx._ +import sifive.fpgashells.clocks._ +import sifive.fpgashells.devices.xilinx.xilinxvc709mig.{XilinxVC709MIGPads, XilinxVC709MIGParams, XilinxVC709MIG} + +class MemClockVC709PlacedOverlay(val shell: VC709ShellBasicOverlays, name: String, val designInput: ClockInputDesignInput, val shellInput: ClockInputShellInput) + extends LVDSClockInputXilinxPlacedOverlay(name, designInput, shellInput) +{ + val node = shell { ClockSourceNode(freqMHz = 233.3333, jitterPS = 50)(ValName(name)) } + + shell { InModuleBody { + shell.xdc.addPackagePin(io.p, "AY18") + shell.xdc.addPackagePin(io.n, "AY17") + shell.xdc.addIOStandard(io.p, "DIFF_SSTL15_DCI") + shell.xdc.addIOStandard(io.n, "DIFF_SSTL15_DCI") + } } +} +class MemClockVC709ShellPlacer(shell: VC709ShellBasicOverlays, val shellInput: ClockInputShellInput)(implicit val valName: ValName) + extends ClockInputShellPlacer[VC709ShellBasicOverlays] +{ + def place(designInput: ClockInputDesignInput) = new MemClockVC709PlacedOverlay(shell, valName.name, designInput, shellInput) +} + +class GPIOVC709PlacedOverlay(val shell: VC709ShellBasicOverlays, name: String, val designInput: GPIODesignInput, val shellInput: GPIOShellInput, gpioNames: Seq[String]) + extends GPIOXilinxPlacedOverlay(name, designInput, shellInput) +{ + shell { InModuleBody { + require(gpioNames.length == io.gpio.length) + + val packagePinsWithIOStdWithPackageIOs = (gpioNames zip io.gpio).map { case (name, io) => + val (pin, iostd) = GPIOs.pinMapping(name) + (pin, iostd, IOPin(io)) + } + + packagePinsWithIOStdWithPackageIOs foreach { case (pin, iostd, io) => { + shell.xdc.addPackagePin(io, pin) + shell.xdc.addIOStandard(io, iostd) + } } + } } +} +class GPIOVC709ShellPlacer(val shell: VC709ShellBasicOverlays, val shellInput: GPIOShellInput, gpioNames: Seq[String])(implicit val valName: ValName) + extends GPIOShellPlacer[VC709ShellBasicOverlays] { + def place(designInput: GPIODesignInput) = new GPIOVC709PlacedOverlay(shell, valName.name, designInput, shellInput, gpioNames) +} + +class DualDDR3VC709PlacedOverlay(val shell: VC709FPGATestHarness, name: String, val designInput: DDRDesignInput, val shellInput: DDRShellInput) + extends DDR3XilinxPlacedOverlay(shell, name, designInput, shellInput) +{ + // since this uses a separate clk/rst need to put an async crossing + val asyncSink = LazyModule(new TLAsyncCrossingSink()) + val migClkRstNode = BundleBridgeSource(() => new Bundle { + val clock = Output(Clock()) + val reset = Output(Bool()) + }) + val topMigClkRstIONode = shell { migClkRstNode.makeSink() } + + InModuleBody { + // setup async crossing + asyncSink.module.clock := migClkRstNode.bundle.clock + asyncSink.module.reset := migClkRstNode.bundle.reset + } + + shell { InModuleBody { + require (shell.mem_clock.get.isDefined, "Use of DualDDR3VC709PlacedOverlay depends on MemClockVC709PlacedOverlay") + + val (sys, _) = shell.mem_clock.get.get.overlayOutput.node.out(0) + val (ui, _) = ddrUI.out(0) + val (ar, _) = areset.in(0) + + // connect the async fifo sync to sys_clock + topMigClkRstIONode.bundle.clock := sys.clock + topMigClkRstIONode.bundle.reset := sys.reset + + val port = topIONode.bundle.port + io <> port + // This is modified for vc709 + ui.clock := port.ui_clk + ui.reset := !port.mmcm_locked || port.ui_clk_sync_rst + port.sys_clk_i := sys.clock.asUInt + port.sys_rst := sys.reset // pllReset + port.aresetn := !ar.reset + } } + + shell.sdc.addGroup(pins = Seq(mig.island.module.blackbox.io.ui_clk)) +} +class DualDDR3VC709ShellPlacer(shell: VC709FPGATestHarness, val shellInput: DDRShellInput)(implicit val valName: ValName) + extends DDRShellPlacer[VC709FPGATestHarness] { + def place(designInput: DDRDesignInput) = new DualDDR3VC709PlacedOverlay(shell, valName.name, designInput, shellInput) +} \ No newline at end of file diff --git a/fpga/src/main/scala/vc709/DigitalTop.scala b/fpga/src/main/scala/vc709/DigitalTop.scala new file mode 100644 index 0000000000..6d87e9dc83 --- /dev/null +++ b/fpga/src/main/scala/vc709/DigitalTop.scala @@ -0,0 +1,74 @@ +package chipyard.fpga.vc709 + +import chisel3._ +import chisel3.util._ + +import freechips.rocketchip.system._ +import freechips.rocketchip.subsystem._ +import freechips.rocketchip.devices.debug._ +import freechips.rocketchip.config.Parameters +import freechips.rocketchip.devices.tilelink._ +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.interrupts._ + +import chipyard.{DigitalTop, DigitalTopModule} + +import sifive.blocks.devices.gpio._ +import sifive.blocks.devices.pinctrl.{BasePin} + +import sifive.fpgashells.shell._ +import sifive.fpgashells.clocks._ + +object PinGen { + def apply(): BasePin = { + new BasePin() + } +} + +trait HasXilinxVC709PCIe { this: BaseSubsystem => + /*** The second clock goes to the PCIe ***/ + val memClkNode = p(ClockInputOverlayKey).last.place(ClockInputDesignInput()).overlayOutput.node + val harnessMemPLL = p(PLLFactoryKey)() + val memGroup = ClockGroup() + val memWrangler = LazyModule(new ResetWrangler) + val memClock = ClockSinkNode(freqMHz = p(FPGAFrequencyKey)) + + memClock := memWrangler.node := memGroup := harnessMemPLL := memClkNode + + /*** Instantiate PCIe Module ***/ + p(PCIeOverlayKey).zipWithIndex.map { case (key, i) => + val overlayOutput = key.place(PCIeDesignInput(wrangler=memWrangler.node, corePLL=harnessMemPLL)).overlayOutput + val (pcieNode: TLNode, intNode: IntOutwardNode) = (overlayOutput.pcieNode, overlayOutput.intNode) + val (slaveTLNode: TLIdentityNode, masterTLNode: TLAsyncSinkNode) = (pcieNode.inward, pcieNode.outward) + fbus.coupleFrom(s"master_named_pcie${i}"){ _ :=* TLFIFOFixer(TLFIFOFixer.all) :=* masterTLNode } + pbus.coupleTo(s"slave_named_pcie${i}"){ slaveTLNode :*= TLWidthWidget(pbus.beatBytes) :*= _ } + ibus.fromSync := intNode + } +} + +trait HasChosenNodeInDTS { this: BaseSubsystem => + // Work-around for a kernel bug (command-line ignored if /chosen missing) + val chosen = new DeviceSnippet { + def describe() = Description("chosen", Map()) + } +} + +// ------------------------------------ +// VC709 DigitalTop +// ------------------------------------ + +// DOC include start: VC709DigitalTop +class VC709DigitalTop()(implicit p: Parameters) extends DigitalTop + with sifive.blocks.devices.i2c.HasPeripheryI2C // Enables optionally adding the sifive I2C + with freechips.rocketchip.devices.debug.HasPeripheryDebug + with HasXilinxVC709PCIe + with HasChosenNodeInDTS +{ + override lazy val module = new VC709DigitalTopModule(this) +} + +class VC709DigitalTopModule[+L <: VC709DigitalTop](l: L) extends DigitalTopModule(l) + with sifive.blocks.devices.i2c.HasPeripheryI2CModuleImp + with freechips.rocketchip.devices.debug.HasPeripheryDebugModuleImp +// DOC include end: VC709DigitalTop \ No newline at end of file diff --git a/fpga/src/main/scala/vc709/GPIOs.scala b/fpga/src/main/scala/vc709/GPIOs.scala new file mode 100644 index 0000000000..0dc9950d31 --- /dev/null +++ b/fpga/src/main/scala/vc709/GPIOs.scala @@ -0,0 +1,38 @@ +package chipyard.fpga.vc709 + +import scala.collection.mutable.{LinkedHashMap} + +object GPIOs { + + // map of the pin name (akin to die pin name) to (fpga package pin, IOSTANDARD) + val pinMapping = LinkedHashMap( + // these connect to LEDs and switches on the VC709 (and use 1.8V) + "led0" -> ("AM39", "LVCMOS18"), // 0 + "led1" -> ("AN39", "LVCMOS18"), // 1 + "led2" -> ("AR37", "LVCMOS18"), // 2 + "led3" -> ("AT37", "LVCMOS18"), // 3 + "led4" -> ("AR35", "LVCMOS18"), // 4 + "led5" -> ("AP41", "LVCMOS18"), // 5 + "led6" -> ("AP42", "LVCMOS18"), // 6 + "led7" -> ("AU39", "LVCMOS18"), // 7 + "sw_n" -> ("AR40", "LVCMOS18"), // N + "sw_e" -> ("AU38", "LVCMOS18"), // E + "sw_s" -> ("AP40", "LVCMOS18"), // S + "sw_w" -> ("AW40", "LVCMOS18"), // W + "sw_c" -> ("AV39", "LVCMOS18"), // C + "sw_0" -> ("AV30", "LVCMOS18"), // 0 + "sw_1" -> ("AY33", "LVCMOS18"), // 1 + "sw_2" -> ("BA31", "LVCMOS18"), // 2 + "sw_3" -> ("BA32", "LVCMOS18"), // 3 + "sw_4" -> ("AW30", "LVCMOS18"), // 4 + "sw_5" -> ("AY30", "LVCMOS18"), // 5 + "sw_6" -> ("BA30", "LVCMOS18"), // 6 + "sw_7" -> ("BB31", "LVCMOS18") // 7 + ) + + // return list of names (ordered) + def names: Seq[String] = pinMapping.keys.toSeq + + // return number of GPIOs + def width: Int = pinMapping.size +} diff --git a/fpga/src/main/scala/vc709/HarnessBinders.scala b/fpga/src/main/scala/vc709/HarnessBinders.scala new file mode 100644 index 0000000000..a9053ce8a4 --- /dev/null +++ b/fpga/src/main/scala/vc709/HarnessBinders.scala @@ -0,0 +1,65 @@ +package chipyard.fpga.vc709 + +import chisel3._ +import chisel3.experimental.{BaseModule} + +import freechips.rocketchip.diplomacy.{NodeHandlePair} +import freechips.rocketchip.util.{HeterogeneousBag} +import freechips.rocketchip.tilelink.{TLBundle} + +import sifive.blocks.devices.i2c.{HasPeripheryI2CModuleImp, I2CPort} +import sifive.blocks.devices.uart.{HasPeripheryUARTModuleImp, UARTPortIO} +import sifive.blocks.devices.gpio.{HasPeripheryGPIOModuleImp, GPIOPortIO} +import sifive.fpgashells.devices.xilinx.xilinxvc709pciex1.{HasSystemXilinxVC709PCIeX1ModuleImp, XilinxVC709PCIeX1IO} + +import chipyard.{HasHarnessSignalReferences, CanHaveMasterTLMemPort} +import chipyard.harness.{OverrideHarnessBinder} + +/*** I2C ***/ +class WithI2C extends OverrideHarnessBinder({ + (system: HasPeripheryI2CModuleImp, th: BaseModule with HasHarnessSignalReferences, ports: Seq[I2CPort]) => { + th match { case vc709th: VC709FPGATestHarnessImp => { + require(ports.size == 1) + + vc709th.vc709Outer.io_i2c_bb.bundle <> ports.head + } } + } +}) + +/*** UART ***/ +class WithUART extends OverrideHarnessBinder({ + (system: HasPeripheryUARTModuleImp, th: BaseModule with HasHarnessSignalReferences, ports: Seq[UARTPortIO]) => { + th match { case vc709th: VC709FPGATestHarnessImp => { + require(ports.size == 1) + + vc709th.vc709Outer.io_uart_bb.bundle <> ports.head + } } + } +}) + +/*** GPIO ***/ +class WithGPIO extends OverrideHarnessBinder({ + (system: HasPeripheryGPIOModuleImp, th: BaseModule with HasHarnessSignalReferences, ports: Seq[GPIOPortIO]) => { + th match { case vc709th: VC709FPGATestHarnessImp => { + require(ports.size == 1) + + vc709th.vc709Outer.io_gpio_bb.bundle <> ports.head + } } + } +}) + +/*** Experimental DDR ***/ +class WithDDRMem extends OverrideHarnessBinder({ + (system: CanHaveMasterTLMemPort, th: BaseModule with HasHarnessSignalReferences, ports: Seq[HeterogeneousBag[TLBundle]]) => { + th match { case vc709th: VC709FPGATestHarnessImp => { + require(ports.size > 0, "There must be at least one port.") // all ports go to the TL mem + // connect each ddrClient to port + (ports zip vc709th.vc709Outer.ddrClients).map { case (port, ddrClient) => + val bundles = ddrClient.out.map(_._1) + val wire = Wire(new HeterogeneousBag(bundles.map(_.cloneType))) + (bundles zip wire) foreach { case (bundle, io) => bundle <> io } + wire <> port + } + } } + } +}) \ No newline at end of file diff --git a/fpga/src/main/scala/vc709/TestHarness.scala b/fpga/src/main/scala/vc709/TestHarness.scala new file mode 100644 index 0000000000..c41a2f5324 --- /dev/null +++ b/fpga/src/main/scala/vc709/TestHarness.scala @@ -0,0 +1,144 @@ +package chipyard.fpga.vc709 + +import chisel3._ +import chisel3.experimental.{IO} + +import freechips.rocketchip.diplomacy._ +import freechips.rocketchip.config._ +import freechips.rocketchip.subsystem._ +import freechips.rocketchip.tilelink._ +import freechips.rocketchip.interrupts._ + +import sifive.fpgashells.shell.xilinx._ +import sifive.fpgashells.ip.xilinx._ +import sifive.fpgashells.shell._ +import sifive.fpgashells.clocks._ + +import sifive.blocks.devices.i2c._ +import sifive.blocks.devices.uart._ +import sifive.blocks.devices.gpio._ + +import chipyard.{HasHarnessSignalReferences, HasTestHarnessFunctions, BuildTop, ChipTop, ExtTLMem, CanHaveMasterTLMemPort} +import chipyard.iobinders.{HasIOBinders} +import chipyard.harness.{ApplyHarnessBinders} + +case object FPGAFrequencyKey extends Field[Double](50.0) + +class VC709FPGATestHarness(override implicit val p: Parameters) extends VC709ShellBasicOverlays { + + def dp = designParameters + + // Order matters; ddr depends on sys_clock + val mem_clock = Overlay(ClockInputOverlayKey, new MemClockVC709ShellPlacer(this, ClockInputShellInput())) + val gpio = Overlay(GPIOOverlayKey, new GPIOVC709ShellPlacer(this, GPIOShellInput(), GPIOs.names)) + // val ddr1 = Overlay(DDROverlayKey, new DDR3VC709ShellPlacer(this, DDRShellInput())) + + val topDesign = LazyModule(p(BuildTop)(dp)).suggestName("chiptop") + +// DOC include start: ClockOverlay + + require(dp(ClockInputOverlayKey).size > 0, "There must be at least one sysclk.") + /*** Connect/Generate clocks ***/ + // place all clocks in the shell, and connect to the PLL that will generate + // multiple clocks, finally create and connect to the clockSinkNode + + /*** The first clock goes to the system and the first DDR ***/ + val sysClkNode = dp(ClockInputOverlayKey).head.place(ClockInputDesignInput()).overlayOutput.node + val harnessSysPLL = dp(PLLFactoryKey)() + val dutGroup = ClockGroup() + val dutWrangler = LazyModule(new ResetWrangler) + val dutClock = ClockSinkNode(freqMHz = dp(FPGAFrequencyKey)) + + dutClock := dutWrangler.node := dutGroup := harnessSysPLL := sysClkNode + +// DOC include end: ClockOverlay + + /*** I2C ***/ + + // 1st I2C goes to the VC709 dedicated I2C + val io_i2c_bb = BundleBridgeSource(() => (new I2CPort)) + dp(I2COverlayKey).head.place(I2CDesignInput(io_i2c_bb)) + + /*** UART ***/ + +// DOC include start: UartOverlay + // 1st UART goes to the VC709 dedicated UART + + val io_uart_bb = BundleBridgeSource(() => (new UARTPortIO(dp(PeripheryUARTKey).head))) + dp(UARTOverlayKey).head.place(UARTDesignInput(io_uart_bb)) +// DOC include end: UartOverlay + + /*** LEDs / GPIO ***/ +// DOC include start: GPIOOverlay + // 1st GPIO goes to the VC709 dedicated GPIO + + val gpioParams = dp(PeripheryGPIOKey).head + val io_gpio_bb = BundleBridgeSource(() => (new GPIOPortIO(gpioParams))) + dp(GPIOOverlayKey).head.place(GPIODesignInput(gpioParams, io_gpio_bb)) +// DOC include end: GPIOOverlay + + /*** DDR ***/ + +// DOC include start: DDR3Overlay + // All DDR3s use the same clock + + var ddrDesignInput = DDRDesignInput(dp(ExtTLMem).get.master.base, dutWrangler.node, harnessSysPLL) + val ddrClients = topDesign match { case td: ChipTop => + td.lazySystem match { case lsys: CanHaveMasterTLMemPort => + (dp(DDROverlayKey) zip lsys.memTLNode.edges.in).map { case (ddrOverlay, edge) => + val ddtNode = ddrOverlay.place(ddrDesignInput).overlayOutput.ddr + val ddrClient = TLClientNode(Seq(edge.master)) + ddtNode := ddrClient + ddrClient + } + } + } +// DOC include end: DDR3Overlay + + // module implementation + override lazy val module = new VC709FPGATestHarnessImp(this) +} + +class VC709FPGATestHarnessImp(_outer: VC709FPGATestHarness) extends LazyRawModuleImp(_outer) with HasHarnessSignalReferences { + + val vc709Outer = _outer + + val reset = IO(Input(Bool())) + _outer.xdc.addPackagePin(reset, "AV40") + _outer.xdc.addIOStandard(reset, "LVCMOS18") + + val resetIBUF = Module(new IBUF) + resetIBUF.io.I := reset + + val sysclk: Clock = _outer.sysClkNode.out.head._1.clock + + val powerOnReset: Bool = PowerOnResetFPGAOnly(sysclk) + _outer.sdc.addAsyncPath(Seq(powerOnReset)) + + val ereset: Bool = _outer.chiplink.get() match { + case Some(x: ChipLinkVC709PlacedOverlay) => !x.ereset_n + case _ => false.B + } + + _outer.pllReset := (resetIBUF.io.O || powerOnReset || false.B) + + // reset setup + val hReset = Wire(Reset()) + hReset := _outer.dutClock.in.head._1.reset + + val harnessClock = _outer.dutClock.in.head._1.clock + val harnessReset = WireInit(hReset) + val dutReset = hReset.asAsyncReset + val success = false.B + + childClock := harnessClock + childReset := harnessReset + + // harness binders are non-lazy + _outer.topDesign match { case d: HasTestHarnessFunctions => + d.harnessFunctions.foreach(_(this)) + } + _outer.topDesign match { case d: HasIOBinders => + ApplyHarnessBinders(this, d.lazySystem, d.portMap) + } +}