@ -0,0 +1,71 @@ | |||||
## Vexriscv with AES128 ECB on the memory mapped bus | |||||
### In the simulation: | |||||
From inside the folder ```vexriscv``` start the simulation with | |||||
``` | |||||
sbt "runMain quantumrisc.PQVexRiscvSim" | |||||
``` | |||||
Connect ```openocd-vexriscv``` to the simulation. Therefore open a new console, | |||||
navigate again to the ```vexriscv``` folder and go with | |||||
``` | |||||
openocd-vexriscv -f vexriscvsim.cfg | |||||
``` | |||||
Now you might want to compile and upload the C code to the simulation. | |||||
Compile the C project from inside the folder ```c_project``` with | |||||
``` | |||||
make PLATFORM=pqvexriscvsim | |||||
``` | |||||
The compilation has created a file with the name ```main.elf```. | |||||
Upload this file via GDB to the simulation | |||||
(from inside the folder ```c_project```) with | |||||
``` | |||||
riscv64-unknown-elf-gdb -tui -ex 'set remotetimeout 30' -ex 'target extended-remote :3333' -ex 'load' -ex 'break main' -ex 'continue' main.elf | |||||
``` | |||||
One breakpoint at the start of the main function is already set. You might want to set another one inside the While(1) loop. Mabye at line 70 with: | |||||
``` | |||||
b 70 | |||||
``` | |||||
Continuing the next breakpoint works with ```c```. | |||||
The UART output from the Vexriscv is visible in the first concolse, where you started the simulation. | |||||
### On the ULX3S Board: | |||||
``` | |||||
sbt "runMain quantumrisc.PQVexRiscvUlx3s" | |||||
``` | |||||
Copy the created ```*ulx3s.v``` verilog file and the memory files ```*ulx3s*.bin``` to the folder ```ulx3s```. From there run ```make``` to create the bitstream: | |||||
``` | |||||
make PQVexRiscvUlx3s.bit | |||||
``` | |||||
Flash the bitstream to the board: | |||||
``` | |||||
sudo fujprog PQVexRiscvUlx3s.bit | |||||
``` | |||||
The Vexrisc is now running on the ULX3S Board. Now connect ```openocd``` to it. To do that, you need an external JTAG Adapter. The one i'm using is a Adafruit TF232H. | |||||
The description for the wiring is available in one of my other projects: | |||||
https://github.com/ThorKn/vexriscv-ulx3s-simple-plugin | |||||
This time you need a different configfile, contained in the ```vexriscv``` folder: | |||||
``` | |||||
sudo openocd-vexriscv -f tf2323h_openocd.cfg | |||||
``` | |||||
Build the C project for a different ```PLATFORM``` then before (in the sim): | |||||
``` | |||||
make PLATFORM=pqvexriscvulx3s | |||||
``` | |||||
Uploading via GDB and using GDB is the same as above: | |||||
``` | |||||
riscv64-unknown-elf-gdb -tui -ex 'set remotetimeout 30' -ex 'target extended-remote :3333' -ex 'load' -ex 'break main' -ex 'continue' main.elf | |||||
``` | |||||
To see the UART output you can use the UART interface from the ULX3S board. Just open a Terminal program of your choice (i.e. GTKTerm) and connect it over USB with the parameters ```115200 - 8 - N - 1```. |
@ -0,0 +1,119 @@ | |||||
# Color definitions ------------------------------- | |||||
NO_COLOR="\033[0m" | |||||
RED="\033[38;5;009m" | |||||
GREEN="\033[38;5;010m" | |||||
YELLOW="\033[38;5;011m" | |||||
ORANGE="\033[38;5;214m" | |||||
LIGHTPURPLE="\033[38;5;177m" | |||||
PURPLE="\033[38;5;135m" | |||||
CYAN="\033[38;5;014m" | |||||
LIGHTBLUE="\033[38;5;39m" | |||||
BLUE="\033[38;5;75m" | |||||
DARKBLUE="\033[38;5;33m" | |||||
LIGHTGRAY="\033[38;5;252m" | |||||
DARKGRAY="\033[38;5;242m" | |||||
BRIGHTRED="\033[91m" | |||||
BOLD="\033[1m" | |||||
# PLATFORM definition, if not given ---------------- | |||||
PLATFORM ?= pqvexriscvsim | |||||
# PATHS (adapt to your system)---------------------- | |||||
PATH_RISCV=/opt/riscv/ | |||||
PATH_RISC_BIN=$(PATH_RISCV)bin/ | |||||
PREFIX?=$(PATH_RISC_BIN)riscv64-unknown-elf | |||||
# Project DIRS ------------------------------------- | |||||
LD_DIR=ld/ | |||||
BSP_DIR=bsp/ | |||||
SRC_DIR=src/ | |||||
INC_DIR=inc/ | |||||
OBJ_DIR=obj/ | |||||
# TOOLS -------------------------------------------- | |||||
AR?=$(PREFIX)-ar | |||||
GCC?=$(PREFIX)-gcc | |||||
CLANG=clang | |||||
GDB?=$(PREFIX)-gdb | |||||
OBJDUMP?=$(PREFIX)-objdump | |||||
OBJCOPY?=$(PREFIX)-objcopy | |||||
RISCVPATH=$(PATH_RISCV)riscv64-unknown-elf | |||||
ECHO?=echo | |||||
# GCC FLAGS --------------------------------------- | |||||
CC=$(GCC) | |||||
GCC_CFLAGS_COMMON := -g \ | |||||
-Os \ | |||||
-fno-builtin-printf \ | |||||
-Wno-unused-parameter \ | |||||
-Wall -Wextra -Wredundant-decls \ | |||||
-Wshadow -Wno-unused-function \ | |||||
-fno-common \ | |||||
-I$(RISCVPATH)/include \ | |||||
-I$(INC_DIR) | |||||
GCC_CFLAGS_MURAX=-fstrict-volatile-bitfields --specs=nosys.specs | |||||
RISCV_ARCH?=rv32im | |||||
RISCV_ABI?=ilp32 | |||||
RISCV_CMODEL?=medany | |||||
RISCV_ARCHFLAGS +=-march=$(RISCV_ARCH) | |||||
RISCV_ARCHFLAGS +=-mabi=$(RISCV_ABI) | |||||
RISCV_ARCHFLAGS +=-mcmodel=$(RISCV_CMODEL) | |||||
GCC_RISCV_ARCHFLAGS=$(RISCV_ARCHFLAGS) | |||||
CFLAGS += $(GCC_CFLAGS_COMMON) \ | |||||
$(GCC_CFLAGS_MURAX) \ | |||||
$(GCC_RISCV_ARCHFLAGS) | |||||
# Linker flags -------------------------------------- | |||||
LDSCRIPT = $(LD_DIR)$(PLATFORM).ld | |||||
LDFLAGS = $(GCC_RISCV_ARCHFLAGS) | |||||
LDFLAGS += --specs=nosys.specs | |||||
LDFLAGS += -Wl,-T$(LDSCRIPT) | |||||
LDFLAGS += -nostartfiles -ffreestanding -Wl,--gc-sections | |||||
LDFLAGS += -L$(BSP_DIR) | |||||
LDFLAGS += -Wl,--start-group -l$(PLATFORM)bsp -lc -lm -Wl,--end-group | |||||
# Object files -------------------------------------- | |||||
SDK_ASM_SRCS := $(wildcard $(SRC_DIR)*.S) | |||||
SDK_C_SRCS := $(wildcard $(SRC_DIR)*.c) | |||||
SDK_ASM_OBJS := $(SDK_ASM_SRCS:$(SRC_DIR)%.S=$(OBJ_DIR)%.o) | |||||
SDK_C_OBJS := $(SDK_C_SRCS:$(SRC_DIR)%.c=$(OBJ_DIR)%.o) | |||||
SDK_OBJS := $(SDK_C_OBJS) $(SDK_ASM_OBJS) | |||||
# Target all ---------------------------------------- | |||||
.PHONY: all | |||||
all: $(BSP_DIR)lib$(PLATFORM)bsp.a main.hex main.bin | |||||
# Targets -------------------------------------------- | |||||
main.bin: main.elf | |||||
@$(ECHO) $(PURPLE)"obj "$@""$(LIGHTGRAY) | |||||
$(OBJCOPY) -O binary $< $@ | |||||
main.hex: main.elf | |||||
@$(ECHO) $(PURPLE)"obj "$@""$(LIGHTGRAY) | |||||
$(OBJCOPY) -O ihex $< $@ | |||||
main.elf: $(LIBWRAP) $(SDK_OBJS) $(LDSCRIPT) | |||||
@$(ECHO) $(LIGHTPURPLE)"building "$@""$(LIGHTGRAY) | |||||
$(GCC) $(GCC_RISCV_ARCHFLAGS) $(GCC_CFLAGS_COMMON) $(SDK_OBJS) -o $@ $(LDFLAGS) | |||||
$(SDK_ASM_OBJS): $(OBJ_DIR)%.o: $(SRC_DIR)%.S | |||||
@$(ECHO) $(ORANGE)"building "$@" (.S)"$(LIGHTGRAY) | |||||
$(CC) $(CFLAGS) -c -o $@ $^ | |||||
$(SDK_C_OBJS): $(OBJ_DIR)%.o: $(SRC_DIR)%.c | |||||
@$(ECHO) $(ORANGE)"building "$@" (.c)"$(LIGHTGRAY) | |||||
$(CC) $(CFLAGS) -c -o $@ $^ | |||||
$(BSP_DIR)lib$(PLATFORM)bsp.a: | |||||
@$(ECHO) $(ORANGE)"building "$@" (.a)"$(LIGHTGRAY) | |||||
cd $(BSP_DIR) && $(MAKE) | |||||
.PHONY: clean | |||||
clean: | |||||
@$(ECHO) $(RED)"cleaning..."$(LIGHTGRAY) | |||||
rm -f main.elf main.bin main.hex $(SDK_OBJS) | |||||
cd $(BSP_DIR) && $(MAKE) clean |
@ -0,0 +1,50 @@ | |||||
PLATFORM ?= pqvexriscvsim | |||||
RISCV_ARCH ?= rv32im | |||||
RISCV_ABI ?= ilp32 | |||||
RISCV_CMODEL ?= medany | |||||
RISCV_ARCHFLAGS += -march=$(RISCV_ARCH) | |||||
RISCV_ARCHFLAGS += -mabi=$(RISCV_ABI) | |||||
RISCV_ARCHFLAGS += -mcmodel=$(RISCV_CMODEL) | |||||
CFLAGS += -Os -Wall -Wextra | |||||
CFLAGS += $(RISCV_ARCHFLAGS) | |||||
CFLAGS += -fstrict-volatile-bitfields | |||||
CFLAGS += --specs=nosys.specs | |||||
ifeq ($(PLATFORM),pqvexriscvup5k) | |||||
CFLAGS += -DVEXRISCV_VOLATILE -DVEXRISCV_RWMTVEC | |||||
endif | |||||
ifeq ($(PLATFORM),pqvexriscvulx3s) | |||||
CFLAGS += -DVEXRISCV_VOLATILE -DVEXRISCV_RWMTVEC | |||||
endif | |||||
ifeq ($(PLATFORM),pqvexriscvicoboard) | |||||
CFLAGS += -DVEXRISCV_VOLATILE -DVEXRISCV_RWMTVEC | |||||
endif | |||||
ifeq ($(PLATFORM),pqvexriscvsim) | |||||
CFLAGS += -DVEXRISCV_RWMTVEC | |||||
endif | |||||
CROSS_PREFIX ?= riscv64-unknown-elf | |||||
CC = $(CROSS_PREFIX)-gcc | |||||
AR = $(CROSS_PREFIX)-ar | |||||
SRCS = init.c start.s | |||||
OBJS = $(PLATFORM)_init.o $(PLATFORM)_start.o | |||||
TARGET = lib$(PLATFORM)bsp.a | |||||
all: $(TARGET) | |||||
$(TARGET): $(OBJS) | |||||
$(AR) rcs $@ $^ | |||||
$(PLATFORM)_%.o: %.c | |||||
$(CC) $(CFLAGS) -c -o $@ $< | |||||
$(PLATFORM)_%.o: %.S | |||||
$(CC) $(CFLAGS) -c -o $@ $< | |||||
clean: | |||||
rm -rf $(OBJS) $(TARGET) |
@ -0,0 +1,8 @@ | |||||
#include "weak_under_alias.h" | |||||
void __weak__init() {} | |||||
void __weak__fini() {} | |||||
weak_under_alias(_init); | |||||
weak_under_alias(_fini); |
@ -0,0 +1,156 @@ | |||||
.section .init | |||||
.global _start | |||||
.type _start,@function | |||||
_start: | |||||
#ifndef VEXRISCV_RWMTVEC | |||||
j _crtInit | |||||
nop | |||||
nop | |||||
nop | |||||
nop | |||||
nop | |||||
nop | |||||
nop | |||||
j trap_entry | |||||
_crtInit: | |||||
#endif | |||||
.cfi_startproc | |||||
.cfi_undefined ra | |||||
.option push | |||||
.option norelax | |||||
la gp, __global_pointer$ | |||||
.option pop | |||||
la sp, _sp | |||||
#ifndef VEXRISCV_VOLATILE | |||||
/* Load data section */ | |||||
la a0, _data_lma | |||||
la a1, _data | |||||
la a2, _edata | |||||
bgeu a1, a2, 2f | |||||
1: | |||||
lw t0, (a0) | |||||
sw t0, (a1) | |||||
addi a0, a0, 4 | |||||
addi a1, a1, 4 | |||||
bltu a1, a2, 1b | |||||
2: | |||||
#endif | |||||
/* Clear bss section */ | |||||
la a0, __bss_start | |||||
la a1, _end | |||||
bgeu a0, a1, 2f | |||||
1: | |||||
sw zero, (a0) | |||||
addi a0, a0, 4 | |||||
bltu a0, a1, 1b | |||||
2: | |||||
/* Call global constructors */ | |||||
la a0, __libc_fini_array | |||||
call atexit | |||||
call __libc_init_array | |||||
auipc ra, 0 | |||||
addi sp, sp, -16 | |||||
sw ra, 8(sp) | |||||
/* Enable Interrupts and set trap vector */ | |||||
#ifndef VEXRISCV_RWMTVEC | |||||
la a0, trap_entry | |||||
csrw mtvec, a0 | |||||
#endif | |||||
li a0, 0x880 //880 enable timer + external interrupts | |||||
csrw mie, a0 | |||||
li a0, 0x1808 //1808 enable interrupts | |||||
csrw mstatus, a0 | |||||
/* argc = argv = 0 */ | |||||
li a0, 0 | |||||
li a1, 0 | |||||
call main | |||||
tail exit | |||||
1: | |||||
j 1b | |||||
.cfi_endproc | |||||
.align 4 | |||||
.weak trap_entry | |||||
.global trap_entry | |||||
trap_entry: | |||||
addi sp, sp, -32*4 | |||||
sw x1, 1*4(sp) | |||||
sw x2, 2*4(sp) | |||||
sw x3, 3*4(sp) | |||||
sw x4, 4*4(sp) | |||||
sw x5, 5*4(sp) | |||||
sw x6, 6*4(sp) | |||||
sw x7, 7*4(sp) | |||||
sw x8, 8*4(sp) | |||||
sw x9, 9*4(sp) | |||||
sw x10, 10*4(sp) | |||||
sw x11, 11*4(sp) | |||||
sw x12, 12*4(sp) | |||||
sw x13, 13*4(sp) | |||||
sw x14, 14*4(sp) | |||||
sw x15, 15*4(sp) | |||||
sw x16, 16*4(sp) | |||||
sw x17, 17*4(sp) | |||||
sw x18, 18*4(sp) | |||||
sw x19, 19*4(sp) | |||||
sw x20, 20*4(sp) | |||||
sw x21, 21*4(sp) | |||||
sw x22, 22*4(sp) | |||||
sw x23, 23*4(sp) | |||||
sw x24, 24*4(sp) | |||||
sw x25, 25*4(sp) | |||||
sw x26, 26*4(sp) | |||||
sw x27, 27*4(sp) | |||||
sw x28, 28*4(sp) | |||||
sw x29, 29*4(sp) | |||||
sw x30, 30*4(sp) | |||||
sw x31, 31*4(sp) | |||||
call irqCallback | |||||
lw x1, 1*4(sp) | |||||
lw x2, 2*4(sp) | |||||
lw x3, 3*4(sp) | |||||
lw x4, 4*4(sp) | |||||
lw x5, 5*4(sp) | |||||
lw x6, 6*4(sp) | |||||
lw x7, 7*4(sp) | |||||
lw x8, 8*4(sp) | |||||
lw x9, 9*4(sp) | |||||
lw x10, 10*4(sp) | |||||
lw x11, 11*4(sp) | |||||
lw x12, 12*4(sp) | |||||
lw x13, 13*4(sp) | |||||
lw x14, 14*4(sp) | |||||
lw x15, 15*4(sp) | |||||
lw x16, 16*4(sp) | |||||
lw x17, 17*4(sp) | |||||
lw x18, 18*4(sp) | |||||
lw x19, 19*4(sp) | |||||
lw x20, 20*4(sp) | |||||
lw x21, 21*4(sp) | |||||
lw x22, 22*4(sp) | |||||
lw x23, 23*4(sp) | |||||
lw x24, 24*4(sp) | |||||
lw x25, 25*4(sp) | |||||
lw x26, 26*4(sp) | |||||
lw x27, 27*4(sp) | |||||
lw x28, 28*4(sp) | |||||
lw x29, 29*4(sp) | |||||
lw x30, 30*4(sp) | |||||
lw x31, 31*4(sp) | |||||
addi sp, sp, 32*4 | |||||
mret | |||||
.weak irqCallback | |||||
irqCallback: | |||||
1: | |||||
j 1b |
@ -0,0 +1,7 @@ | |||||
#ifndef WEAK_UNDER_ALIAS_H | |||||
#define WEAK_UNDER_ALIAS_H | |||||
#define weak_under_alias(name) \ | |||||
extern __typeof (__weak_##name) name __attribute__ ((weak, alias ("__weak_"#name))) | |||||
#endif /* WEAK_UNDER_ALIAS_H */ |
@ -0,0 +1,18 @@ | |||||
#ifndef VECRISCV_HAL_H_ | |||||
#define VECRISCV_HAL_H_ | |||||
#include <stddef.h> | |||||
#include <stdint.h> | |||||
#include <sys/types.h> | |||||
#define printf printf_ | |||||
void hal_send(const uint8_t* in, const size_t len); | |||||
void hal_send_str(const char* in); | |||||
int printf_(const char* format, ...); | |||||
void _putchar(char c); | |||||
void _write(int fd, const void* ptr, size_t len); | |||||
#endif /* VECRISCV_HAL_H_ */ |
@ -0,0 +1,121 @@ | |||||
/////////////////////////////////////////////////////////////////////////////// | |||||
// \author (c) Marco Paland (info@paland.com) | |||||
// 2014-2019, PALANDesign Hannover, Germany | |||||
// | |||||
// \license The MIT License (MIT) | |||||
// | |||||
// Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
// of this software and associated documentation files (the "Software"), to deal | |||||
// in the Software without restriction, including without limitation the rights | |||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
// copies of the Software, and to permit persons to whom the Software is | |||||
// furnished to do so, subject to the following conditions: | |||||
// | |||||
// The above copyright notice and this permission notice shall be included in | |||||
// all copies or substantial portions of the Software. | |||||
// | |||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
// THE SOFTWARE. | |||||
// | |||||
// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on | |||||
// embedded systems with a very limited resources. | |||||
// Use this instead of bloated standard/newlib printf. | |||||
// These routines are thread safe and reentrant. | |||||
// | |||||
/////////////////////////////////////////////////////////////////////////////// | |||||
#ifndef _PRINTF_H_ | |||||
#define _PRINTF_H_ | |||||
#include <stdarg.h> | |||||
#include <stddef.h> | |||||
#ifdef __cplusplus | |||||
extern "C" { | |||||
#endif | |||||
#define PRINTF_DISABLE_SUPPORT_FLOAT | |||||
#define PRINTF_DISABLE_SUPPORT_EXPONENTIAL | |||||
// #define PRINTF_DISABLE_SUPPORT_LONG_LONG | |||||
#define PRINTF_DISABLE_SUPPORT_PTRDIFF_T | |||||
/** | |||||
* Output a character to a custom device like UART, used by the printf() function | |||||
* This function is declared here only. You have to write your custom implementation somewhere | |||||
* \param character Character to output | |||||
*/ | |||||
void _putchar(char character); | |||||
/** | |||||
* Tiny printf implementation | |||||
* You have to implement _putchar if you use printf() | |||||
* To avoid conflicts with the regular printf() API it is overridden by macro defines | |||||
* and internal underscore-appended functions like printf_() are used | |||||
* \param format A string that specifies the format of the output | |||||
* \return The number of characters that are written into the array, not counting the terminating null character | |||||
*/ | |||||
#define printf printf_ | |||||
int printf_(const char* format, ...); | |||||
/** | |||||
* Tiny sprintf implementation | |||||
* Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD! | |||||
* \param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output! | |||||
* \param format A string that specifies the format of the output | |||||
* \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character | |||||
*/ | |||||
#define sprintf sprintf_ | |||||
int sprintf_(char* buffer, const char* format, ...); | |||||
/** | |||||
* Tiny snprintf/vsnprintf implementation | |||||
* \param buffer A pointer to the buffer where to store the formatted string | |||||
* \param count The maximum number of characters to store in the buffer, including a terminating null character | |||||
* \param format A string that specifies the format of the output | |||||
* \param va A value identifying a variable arguments list | |||||
* \return The number of characters that COULD have been written into the buffer, not counting the terminating | |||||
* null character. A value equal or larger than count indicates truncation. Only when the returned value | |||||
* is non-negative and less than count, the string has been completely written. | |||||
*/ | |||||
#define snprintf snprintf_ | |||||
#define vsnprintf vsnprintf_ | |||||
int snprintf_(char* buffer, size_t count, const char* format, ...); | |||||
int vsnprintf_(char* buffer, size_t count, const char* format, va_list va); | |||||
/** | |||||
* Tiny vprintf implementation | |||||
* \param format A string that specifies the format of the output | |||||
* \param va A value identifying a variable arguments list | |||||
* \return The number of characters that are WRITTEN into the buffer, not counting the terminating null character | |||||
*/ | |||||
#define vprintf vprintf_ | |||||
int vprintf_(const char* format, va_list va); | |||||
/** | |||||
* printf with output function | |||||
* You may use this as dynamic alternative to printf() with its fixed _putchar() output | |||||
* \param out An output function which takes one character and an argument pointer | |||||
* \param arg An argument pointer for user data passed to output function | |||||
* \param format A string that specifies the format of the output | |||||
* \return The number of characters that are sent to the output function, not counting the terminating null character | |||||
*/ | |||||
int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...); | |||||
#ifdef __cplusplus | |||||
} | |||||
#endif | |||||
#endif // _PRINTF_H_ |
@ -0,0 +1,141 @@ | |||||
/* SECTIONS for non-volatile chip configuration, i.e. chips with flash */ | |||||
SECTIONS | |||||
{ | |||||
.init : | |||||
{ | |||||
KEEP (*(SORT_NONE(.init))) | |||||
} >rom | |||||
.text : | |||||
{ | |||||
*(.text.unlikely .text.unlikely.*) | |||||
*(.text.startup .text.startup.*) | |||||
*(.text .text.*) | |||||
*(.gnu.linkonce.t.*) | |||||
} >rom | |||||
.fini : | |||||
{ | |||||
KEEP (*(SORT_NONE(.fini))) | |||||
} >rom | |||||
PROVIDE (__etext = .); | |||||
PROVIDE (_etext = .); | |||||
PROVIDE (etext = .); | |||||
.rodata : | |||||
{ | |||||
*(.rdata) | |||||
*(.rodata .rodata.*) | |||||
*(.gnu.linkonce.r.*) | |||||
. = ALIGN(8); | |||||
*(.srodata.cst16) | |||||
*(.srodata.cst8) | |||||
*(.srodata.cst4) | |||||
*(.srodata.cst2) | |||||
*(.srodata .srodata.*) | |||||
} >rom | |||||
. = ALIGN(4); | |||||
.preinit_array : | |||||
{ | |||||
PROVIDE_HIDDEN (__preinit_array_start = .); | |||||
KEEP (*(.preinit_array)) | |||||
PROVIDE_HIDDEN (__preinit_array_end = .); | |||||
} >rom | |||||
.init_array : | |||||
{ | |||||
PROVIDE_HIDDEN (__init_array_start = .); | |||||
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) | |||||
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) | |||||
PROVIDE_HIDDEN (__init_array_end = .); | |||||
} >rom | |||||
.fini_array : | |||||
{ | |||||
PROVIDE_HIDDEN (__fini_array_start = .); | |||||
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) | |||||
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) | |||||
PROVIDE_HIDDEN (__fini_array_end = .); | |||||
} >rom | |||||
.ctors : | |||||
{ | |||||
/* gcc uses crtbegin.o to find the start of | |||||
the constructors, so we make sure it is | |||||
first. Because this is a wildcard, it | |||||
doesn't matter if the user does not | |||||
actually link against crtbegin.o; the | |||||
linker won't look for a file to match a | |||||
wildcard. The wildcard also means that it | |||||
doesn't matter which directory crtbegin.o | |||||
is in. */ | |||||
KEEP (*crtbegin.o(.ctors)) | |||||
KEEP (*crtbegin?.o(.ctors)) | |||||
/* We don't want to include the .ctor section from | |||||
the crtend.o file until after the sorted ctors. | |||||
The .ctor section from the crtend file contains the | |||||
end of ctors marker and it must be last */ | |||||
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) | |||||
KEEP (*(SORT(.ctors.*))) | |||||
KEEP (*(.ctors)) | |||||
} >rom | |||||
.dtors : | |||||
{ | |||||
KEEP (*crtbegin.o(.dtors)) | |||||
KEEP (*crtbegin?.o(.dtors)) | |||||
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) | |||||
KEEP (*(SORT(.dtors.*))) | |||||
KEEP (*(.dtors)) | |||||
} >rom | |||||
.lalign : | |||||
{ | |||||
. = ALIGN(4); | |||||
} >rom | |||||
.dalign : | |||||
{ | |||||
. = ALIGN(4); | |||||
} >ram AT>rom | |||||
.data : | |||||
{ | |||||
PROVIDE( _data = . ); | |||||
*(.data .data.*) | |||||
*(.gnu.linkonce.d.*) | |||||
. = ALIGN(8); | |||||
PROVIDE( __global_pointer$ = . + 0x800 ); | |||||
*(.sdata .sdata.*) | |||||
*(.gnu.linkonce.s.*) | |||||
} >ram AT>rom | |||||
PROVIDE( _data_lma = LOADADDR(.data) ); | |||||
. = ALIGN(4); | |||||
PROVIDE( _edata = . ); | |||||
PROVIDE( edata = . ); | |||||
PROVIDE( _fbss = . ); | |||||
PROVIDE( __bss_start = . ); | |||||
.bss : | |||||
{ | |||||
*(.sbss*) | |||||
*(.gnu.linkonce.sb.*) | |||||
*(.bss .bss.*) | |||||
*(.gnu.linkonce.b.*) | |||||
*(COMMON) | |||||
. = ALIGN(4); | |||||
} >ram | |||||
. = ALIGN(8); | |||||
PROVIDE( _end = . ); | |||||
PROVIDE( end = . ); | |||||
} | |||||
PROVIDE(_sp = ORIGIN(ram) + LENGTH(ram)); | |||||
PROVIDE(_heap_end = ORIGIN(ram) + LENGTH(ram)); |
@ -0,0 +1,14 @@ | |||||
OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", "elf32-littleriscv") | |||||
OUTPUT_ARCH(riscv) | |||||
ENTRY( _start ) | |||||
__ram_size = 384K; | |||||
MEMORY | |||||
{ | |||||
rom (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 256K | |||||
ram (wxa!ri) : ORIGIN = 0x80040000, LENGTH = 128K | |||||
} | |||||
INCLUDE ld/nonvolatile.ld |
@ -0,0 +1,14 @@ | |||||
OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", "elf32-littleriscv") | |||||
OUTPUT_ARCH(riscv) | |||||
ENTRY( _start ) | |||||
__ram_size = 384K; | |||||
MEMORY | |||||
{ | |||||
rom (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 256K | |||||
ram (wxa!ri) : ORIGIN = 0x80040000, LENGTH = 128K | |||||
} | |||||
INCLUDE ld/volatile-split.ld |
@ -0,0 +1,14 @@ | |||||
OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", "elf32-littleriscv") | |||||
OUTPUT_ARCH(riscv) | |||||
ENTRY( _start ) | |||||
__ram_size = 384K; | |||||
MEMORY | |||||
{ | |||||
rom (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 256K | |||||
ram (wxa!ri) : ORIGIN = 0x80040000, LENGTH = 128K | |||||
} | |||||
INCLUDE ld/volatile-split.ld |
@ -0,0 +1,137 @@ | |||||
/* SECTIONS for volatile chip configuration, i.e. chips without flash */ | |||||
SECTIONS | |||||
{ | |||||
.init : | |||||
{ | |||||
KEEP (*(SORT_NONE(.init))) | |||||
} >rom | |||||
.text : | |||||
{ | |||||
*(.text.unlikely .text.unlikely.*) | |||||
*(.text.startup .text.startup.*) | |||||
*(.text .text.*) | |||||
*(.gnu.linkonce.t.*) | |||||
} >rom | |||||
.fini : | |||||
{ | |||||
KEEP (*(SORT_NONE(.fini))) | |||||
} >rom | |||||
PROVIDE (__etext = .); | |||||
PROVIDE (_etext = .); | |||||
PROVIDE (etext = .); | |||||
. = ALIGN(4); | |||||
.preinit_array : | |||||
{ | |||||
PROVIDE_HIDDEN (__preinit_array_start = .); | |||||
KEEP (*(.preinit_array)) | |||||
PROVIDE_HIDDEN (__preinit_array_end = .); | |||||
} >rom | |||||
.init_array : | |||||
{ | |||||
PROVIDE_HIDDEN (__init_array_start = .); | |||||
KEEP (*(SORT_BY_INIT_PRIORITY(.init_array.*) SORT_BY_INIT_PRIORITY(.ctors.*))) | |||||
KEEP (*(.init_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .ctors)) | |||||
PROVIDE_HIDDEN (__init_array_end = .); | |||||
} >rom | |||||
.fini_array : | |||||
{ | |||||
PROVIDE_HIDDEN (__fini_array_start = .); | |||||
KEEP (*(SORT_BY_INIT_PRIORITY(.fini_array.*) SORT_BY_INIT_PRIORITY(.dtors.*))) | |||||
KEEP (*(.fini_array EXCLUDE_FILE (*crtbegin.o *crtbegin?.o *crtend.o *crtend?.o ) .dtors)) | |||||
PROVIDE_HIDDEN (__fini_array_end = .); | |||||
} >rom | |||||
.ctors : | |||||
{ | |||||
/* gcc uses crtbegin.o to find the start of | |||||
the constructors, so we make sure it is | |||||
first. Because this is a wildcard, it | |||||
doesn't matter if the user does not | |||||
actually link against crtbegin.o; the | |||||
linker won't look for a file to match a | |||||
wildcard. The wildcard also means that it | |||||
doesn't matter which directory crtbegin.o | |||||
is in. */ | |||||
KEEP (*crtbegin.o(.ctors)) | |||||
KEEP (*crtbegin?.o(.ctors)) | |||||
/* We don't want to include the .ctor section from | |||||
the crtend.o file until after the sorted ctors. | |||||
The .ctor section from the crtend file contains the | |||||
end of ctors marker and it must be last */ | |||||
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) | |||||
KEEP (*(SORT(.ctors.*))) | |||||
KEEP (*(.ctors)) | |||||
} >rom | |||||
.dtors : | |||||
{ | |||||
KEEP (*crtbegin.o(.dtors)) | |||||
KEEP (*crtbegin?.o(.dtors)) | |||||
KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .dtors)) | |||||
KEEP (*(SORT(.dtors.*))) | |||||
KEEP (*(.dtors)) | |||||
} >rom | |||||
.dalign : | |||||
{ | |||||
. = ALIGN(4); | |||||
PROVIDE( _data = . ); | |||||
} >rom | |||||
/* RODATA is usally in ROM, however in volatile configurations this doesn't | |||||
make a lot of sense. */ | |||||
.rodata : | |||||
{ | |||||
*(.rdata) | |||||
*(.rodata .rodata.*) | |||||
*(.gnu.linkonce.r.*) | |||||
} >ram | |||||
.data : | |||||
{ | |||||
*(.data .data.*) | |||||
*(.gnu.linkonce.d.*) | |||||
. = ALIGN(8); | |||||
PROVIDE( __global_pointer$ = . + 0x800 ); | |||||
*(.sdata .sdata.*) | |||||
*(.gnu.linkonce.s.*) | |||||
. = ALIGN(8); | |||||
*(.srodata.cst16) | |||||
*(.srodata.cst8) | |||||
*(.srodata.cst4) | |||||
*(.srodata.cst2) | |||||
*(.srodata .srodata.*) | |||||
} >ram | |||||
. = ALIGN(4); | |||||
PROVIDE( _edata = . ); | |||||
PROVIDE( edata = . ); | |||||
PROVIDE( _fbss = . ); | |||||
PROVIDE( __bss_start = . ); | |||||
.bss : | |||||
{ | |||||
*(.sbss*) | |||||
*(.gnu.linkonce.sb.*) | |||||
*(.bss .bss.*) | |||||
*(.gnu.linkonce.b.*) | |||||
*(COMMON) | |||||
. = ALIGN(4); | |||||
} >ram | |||||
. = ALIGN(8); | |||||
PROVIDE( _end = . ); | |||||
PROVIDE( end = . ); | |||||
} | |||||
PROVIDE(_sp = ORIGIN(ram) + LENGTH(ram)); | |||||
PROVIDE(_heap_end = ORIGIN(ram) + LENGTH(ram)); |
@ -0,0 +1,60 @@ | |||||
#include "hal.h" | |||||
#include <stddef.h> | |||||
/* Murax UART */ | |||||
typedef struct { | |||||
volatile uint32_t DATA; | |||||
volatile uint32_t STATUS; | |||||
volatile uint32_t CLOCK_DIVIDER; | |||||
volatile uint32_t FRAME_CONFIG; | |||||
} Uart_Reg; | |||||
enum UartParity { NONE = 0, EVEN = 1, ODD = 2 }; | |||||
enum UartStop { ONE = 0, TWO = 1 }; | |||||
typedef struct { | |||||
uint32_t dataLength; | |||||
enum UartParity parity; | |||||
enum UartStop stop; | |||||
uint32_t clockDivider; | |||||
} Uart_Config; | |||||
static uint32_t uart_writeAvailability(Uart_Reg* reg) | |||||
{ | |||||
return (reg->STATUS >> 16) & 0xFF; | |||||
} | |||||
static void uart_write(Uart_Reg* reg, uint32_t data) | |||||
{ | |||||
while (uart_writeAvailability(reg) == 0) | |||||
; | |||||
reg->DATA = data; | |||||
} | |||||
#define UART ((Uart_Reg*)(0xF0010000)) | |||||
void hal_send(const uint8_t* in, const size_t len) { | |||||
for (size_t i = 0; i < len; i++) { | |||||
uart_write(UART, in[i]); | |||||
} | |||||
} | |||||
void hal_send_str(const char* in) | |||||
{ | |||||
const char* cur = in; | |||||
while(*cur) { | |||||
uart_write(UART, *cur); | |||||
cur += 1; | |||||
} | |||||
} | |||||
__attribute__((naked)) uint64_t hal_get_time(void) | |||||
{ | |||||
#define LE "\n\t" | |||||
asm volatile (LE"csrr a1, mcycleh" | |||||
LE"csrr a0, mcycle" | |||||
LE"csrr a2, mcycleh" | |||||
LE"bne a1, a2, hal_get_time" | |||||
LE"ret"); | |||||
} |
@ -0,0 +1,21 @@ | |||||
#include <stdio.h> | |||||
#include <stdint.h> | |||||
#include <stddef.h> | |||||
#include <errno.h> | |||||
#undef errno | |||||
extern int errno; | |||||
#include "hal.h" | |||||
void _putchar(char c) { | |||||
hal_send((uint8_t*)&c, 1); | |||||
} | |||||
void _write(int fd, const void* ptr, size_t len) { | |||||
// Don't care about the fd. Just put everything on the UART console. | |||||
(void)fd; | |||||
hal_send(ptr, len); | |||||
} | |||||
@ -0,0 +1,97 @@ | |||||
#include <stdbool.h> | |||||
#include "hal.h" | |||||
#define LENGTH 4 | |||||
typedef struct { | |||||
volatile uint32_t key[LENGTH]; | |||||
volatile uint32_t state_in[LENGTH]; | |||||
volatile uint32_t useEncDec; | |||||
volatile uint32_t valid_in; | |||||
volatile uint32_t valid_out; | |||||
volatile uint32_t state_out[LENGTH]; | |||||
} Aes_Mem; | |||||
#define AES_MEM ((Aes_Mem*)(0xF0030000)) | |||||
void writeKey(uint32_t * key) { | |||||
for (unsigned i = 0; i < LENGTH; i++) { | |||||
AES_MEM->key[i] = key[i]; | |||||
} | |||||
} | |||||
void writeState(uint32_t * state) { | |||||
for (unsigned i = 0; i < LENGTH; i++) { | |||||
AES_MEM->state_in[i] = state[i]; | |||||
} | |||||
} | |||||
void setEncDec(int encDec) { | |||||
AES_MEM->useEncDec = encDec; | |||||
} | |||||
void setValidIn() { | |||||
AES_MEM->valid_in = 1; | |||||
} | |||||
void readState(uint32_t * state) { | |||||
for (unsigned i = 0; i < LENGTH; i++) { | |||||
state[i] = AES_MEM->state_out[i]; | |||||
} | |||||
} | |||||
void printState(uint32_t * state) { | |||||
printf("State:\n"); | |||||
for(unsigned i = 0; i < LENGTH; i++) { | |||||
printf("%08x",state[LENGTH-i-1]); | |||||
} | |||||
printf("\n"); | |||||
} | |||||
void printKey(uint32_t * key) { | |||||
printf("Key:\n"); | |||||
for(unsigned i = 0; i < LENGTH; i++) { | |||||
printf("%08x",key[LENGTH-i-1]); | |||||
} | |||||
printf("\n"); | |||||
} | |||||
int main(void) { | |||||
printf("HELLO WORLD\n"); | |||||
// uint32_t state[LENGTH] = {0x6bc1bee2, 0x2e409f96, 0xe93d7e11, 0x7393172a}; | |||||
// uint32_t key[LENGTH] = {0x2b7e1516, 0x28aed2a6, 0xabf71588, 0x09cf4f3c}; | |||||
uint32_t state[LENGTH] = {0x7393172a, 0xe93d7e11, 0x2e409f96, 0x6bc1bee2}; | |||||
uint32_t key[LENGTH] = {0x09cf4f3c, 0xabf71588, 0x28aed2a6, 0x2b7e1516}; | |||||
writeKey(key); | |||||
while (1) { | |||||
printState(state); | |||||
printKey(key); | |||||
writeState(state); | |||||
setEncDec(1); | |||||
setValidIn(); | |||||
while (AES_MEM->valid_out != 1); | |||||
readState(state); | |||||
printState(state); | |||||
writeState(state); | |||||
setEncDec(0); | |||||
setValidIn(); | |||||
while (AES_MEM->valid_out != 1); | |||||
readState(state); | |||||
printState(state); | |||||
} | |||||
return 0; | |||||
} |
@ -0,0 +1,16 @@ | |||||
/* Public domain. */ | |||||
#include <stddef.h> | |||||
int | |||||
memcmp (const void *str1, const void *str2, size_t count) | |||||
{ | |||||
const unsigned char *s1 = str1; | |||||
const unsigned char *s2 = str2; | |||||
while (count-- > 0) | |||||
{ | |||||
if (*s1++ != *s2++) | |||||
return s1[-1] < s2[-1] ? -1 : 1; | |||||
} | |||||
return 0; | |||||
} |
@ -0,0 +1,61 @@ | |||||
/* Public domain. */ | |||||
#include <stddef.h> | |||||
/* | |||||
** Copyright 2001, Travis Geiselbrecht. All rights reserved. | |||||
** Distributed under the terms of the NewOS License. | |||||
*/ | |||||
/* | |||||
* Copyright (c) 2008 Travis Geiselbrecht | |||||
* | |||||
* Permission is hereby granted, free of charge, to any person obtaining | |||||
* a copy of this software and associated documentation files | |||||
* (the "Software"), to deal in the Software without restriction, | |||||
* including without limitation the rights to use, copy, modify, merge, | |||||
* publish, distribute, sublicense, and/or sell copies of the Software, | |||||
* and to permit persons to whom the Software is furnished to do so, | |||||
* subject to the following conditions: | |||||
* | |||||
* The above copyright notice and this permission notice shall be | |||||
* included in all copies or substantial portions of the Software. | |||||
* | |||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
*/ | |||||
#include <string.h> | |||||
#include <sys/types.h> | |||||
typedef long word; | |||||
#define lsize sizeof(word) | |||||
#define lmask (lsize - 1) | |||||
void *memcpy(void *dest, const void *src, size_t count) | |||||
{ | |||||
char *d = (char *)dest; | |||||
const char *s = (const char *)src; | |||||
int len; | |||||
if(count == 0 || dest == src) | |||||
return dest; | |||||
if(((long)d | (long)s) & lmask) { | |||||
// src and/or dest do not align on word boundary | |||||
if((((long)d ^ (long)s) & lmask) || (count < lsize)) | |||||
len = count; // copy the rest of the buffer with the byte mover | |||||
else | |||||
len = lsize - ((long)d & lmask); // move the ptrs up to a word boundary | |||||
count -= len; | |||||
for(; len > 0; len--) | |||||
*d++ = *s++; | |||||
} | |||||
for(len = count / lsize; len > 0; len--) { | |||||
*(word *)d = *(word *)s; | |||||
d += lsize; | |||||
s += lsize; | |||||
} | |||||
for(len = count & lmask; len > 0; len--) | |||||
*d++ = *s++; | |||||
return dest; | |||||
} |
@ -0,0 +1,64 @@ | |||||
/* | |||||
** Copyright 2005, Michael Noisternig. All rights reserved. | |||||
** Copyright 2001, Travis Geiselbrecht. All rights reserved. | |||||
** Distributed under the terms of the NewOS License. | |||||
*/ | |||||
/* | |||||
* Copyright (c) 2008 Travis Geiselbrecht | |||||
* | |||||
* Permission is hereby granted, free of charge, to any person obtaining | |||||
* a copy of this software and associated documentation files | |||||
* (the "Software"), to deal in the Software without restriction, | |||||
* including without limitation the rights to use, copy, modify, merge, | |||||
* publish, distribute, sublicense, and/or sell copies of the Software, | |||||
* and to permit persons to whom the Software is furnished to do so, | |||||
* subject to the following conditions: | |||||
* | |||||
* The above copyright notice and this permission notice shall be | |||||
* included in all copies or substantial portions of the Software. | |||||
* | |||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | |||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | |||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | |||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | |||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |||||
*/ | |||||
#include <string.h> | |||||
#include <sys/types.h> | |||||
// void * | |||||
// memset(void *s, int c, size_t count) | |||||
// { | |||||
// char *xs = (char *) s; | |||||
// // size_t len = (-(size_t)s) & (sizeof(size_t)-1); | |||||
// // int cc = c & 0xff; | |||||
// // if ( count > len ) { | |||||
// // count -= len; | |||||
// // cc |= cc << 8; | |||||
// // cc |= cc << 16; | |||||
// // // write to non-aligned memory byte-wise | |||||
// // for ( ; len > 0; len-- ) | |||||
// // *xs++ = c; | |||||
// // // write to aligned memory dword-wise | |||||
// // for ( len = count/sizeof(size_t); len > 0; len-- ) { | |||||
// // *((size_t *)xs) = cc; | |||||
// // xs += sizeof(size_t); | |||||
// // } | |||||
// // count &= sizeof(size_t)-1; | |||||
// // } | |||||
// // write remaining bytes | |||||
// for ( ; count > 0; count-- ) | |||||
// *xs++ = (char) c; | |||||
// return s; | |||||
// } | |||||
void * | |||||
memset (void *dest, int val, size_t len) | |||||
{ | |||||
unsigned char *ptr = dest; | |||||
while (len-- > 0) | |||||
*ptr++ = (unsigned char) val; | |||||
return dest; | |||||
} |
@ -0,0 +1,987 @@ | |||||
/////////////////////////////////////////////////////////////////////////////// | |||||
// \author (c) Marco Paland (info@paland.com) | |||||
// 2014-2019, PALANDesign Hannover, Germany | |||||
// | |||||
// \license The MIT License (MIT) | |||||
// | |||||
// Permission is hereby granted, free of charge, to any person obtaining a copy | |||||
// of this software and associated documentation files (the "Software"), to deal | |||||
// in the Software without restriction, including without limitation the rights | |||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||||
// copies of the Software, and to permit persons to whom the Software is | |||||
// furnished to do so, subject to the following conditions: | |||||
// | |||||
// The above copyright notice and this permission notice shall be included in | |||||
// all copies or substantial portions of the Software. | |||||
// | |||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||||
// THE SOFTWARE. | |||||
// | |||||
// \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on | |||||
// embedded systems with a very limited resources. These routines are thread | |||||
// safe and reentrant! | |||||
// Use this instead of the bloated standard/newlib printf cause these use | |||||
// malloc for printf (and may not be thread safe). | |||||
// | |||||
/////////////////////////////////////////////////////////////////////////////// | |||||
#include <stdbool.h> | |||||
#include <stdint.h> | |||||
#include "printf.h" | |||||
// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the | |||||
// printf_config.h header file | |||||
// default: undefined | |||||
#ifdef PRINTF_INCLUDE_CONFIG_H | |||||
#include "printf_config.h" | |||||
#endif | |||||
// 'ntoa' conversion buffer size, this must be big enough to hold one converted | |||||
// numeric number including padded zeros (dynamically created on stack) | |||||
// default: 32 byte | |||||
#ifndef PRINTF_NTOA_BUFFER_SIZE | |||||
#define PRINTF_NTOA_BUFFER_SIZE 32U | |||||
#endif | |||||
// 'ftoa' conversion buffer size, this must be big enough to hold one converted | |||||
// float number including padded zeros (dynamically created on stack) | |||||
// default: 32 byte | |||||
#ifndef PRINTF_FTOA_BUFFER_SIZE | |||||
#define PRINTF_FTOA_BUFFER_SIZE 32U | |||||
#endif | |||||
// support for the floating point type (%f) | |||||
// default: activated | |||||
// #ifndef PRINTF_DISABLE_SUPPORT_FLOAT | |||||
// #define PRINTF_SUPPORT_FLOAT | |||||
// #endif | |||||
// support for exponential floating point notation (%e/%g) | |||||
// default: activated | |||||
// #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL | |||||
// #define PRINTF_SUPPORT_EXPONENTIAL | |||||
// #endif | |||||
// define the default floating point precision | |||||
// default: 6 digits | |||||
// #ifndef PRINTF_DEFAULT_FLOAT_PRECISION | |||||
// #define PRINTF_DEFAULT_FLOAT_PRECISION 6U | |||||
// #endif | |||||
// define the largest float suitable to print with %f | |||||
// default: 1e9 | |||||
// #ifndef PRINTF_MAX_FLOAT | |||||
// #define PRINTF_MAX_FLOAT 1e9 | |||||
// #endif | |||||
// support for the long long types (%llu or %p) | |||||
// default: activated | |||||
#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG | |||||
#define PRINTF_SUPPORT_LONG_LONG | |||||
#endif | |||||
// support for the ptrdiff_t type (%t) | |||||
// ptrdiff_t is normally defined in <stddef.h> as long or long long type | |||||
// default: activated | |||||
// #ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T | |||||
// #define PRINTF_SUPPORT_PTRDIFF_T | |||||
// #endif | |||||
/////////////////////////////////////////////////////////////////////////////// | |||||
// internal flag definitions | |||||
#define FLAGS_ZEROPAD (1U << 0U) | |||||
#define FLAGS_LEFT (1U << 1U) | |||||
#define FLAGS_PLUS (1U << 2U) | |||||
#define FLAGS_SPACE (1U << 3U) | |||||
#define FLAGS_HASH (1U << 4U) | |||||
#define FLAGS_UPPERCASE (1U << 5U) | |||||
#define FLAGS_CHAR (1U << 6U) | |||||
#define FLAGS_SHORT (1U << 7U) | |||||
#define FLAGS_LONG (1U << 8U) | |||||
#define FLAGS_LONG_LONG (1U << 9U) | |||||
#define FLAGS_PRECISION (1U << 10U) | |||||
#define FLAGS_ADAPT_EXP (1U << 11U) | |||||
// import float.h for DBL_MAX | |||||
// #if defined(PRINTF_SUPPORT_FLOAT) | |||||
// #include <float.h> | |||||
// #endif | |||||
// output function type | |||||
typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen); | |||||
// wrapper (used as buffer) for output function type | |||||
typedef struct { | |||||
void (*fct)(char character, void* arg); | |||||
void* arg; | |||||
} out_fct_wrap_type; | |||||
// Sometimes div modulo is not available, we implement one with shift/and/or | |||||
static int divmod(int* Qptr, int* Rptr, const int N, const int D) { | |||||
if (D == 0) { | |||||
return -1; | |||||
} | |||||
int Q = 0; | |||||
int R = 0; | |||||
for (int i = 8*sizeof(int) - 1; i >= 0; i--) { | |||||
R <<= 1; | |||||
R |= (N >> i) & 0x1; | |||||
if (R >= D) { | |||||
R -= D; | |||||
Q |= 1 << i; | |||||
} | |||||
} | |||||
*Qptr = Q; | |||||
*Rptr = R; | |||||
return 0; | |||||
} | |||||
// Sometimes div modulo is not available, we implement one with shift/and/or | |||||
static int divmod_long_long(long long* Qptr, long long* Rptr, const long long N, const int D) { | |||||
if (D == 0) { | |||||
return -1; | |||||
} | |||||
long long Q = 0; | |||||
long long R = 0; | |||||
for (long long i = 8*sizeof(long long) - 1; i >= 0; i--) { | |||||
R <<= 1; | |||||
R |= (N >> i) & 0x1; | |||||
if (R >= D) { | |||||
R -= D; | |||||
Q |= 1 << i; | |||||
} | |||||
} | |||||
*Qptr = Q; | |||||
*Rptr = R; | |||||
return 0; | |||||
} | |||||
// internal buffer output | |||||
static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) | |||||
{ | |||||
if (idx < maxlen) { | |||||
((char*)buffer)[idx] = character; | |||||
} | |||||
} | |||||
// internal null output | |||||
static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) | |||||
{ | |||||
(void)character; (void)buffer; (void)idx; (void)maxlen; | |||||
} | |||||
// internal _putchar wrapper | |||||
static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen) | |||||
{ | |||||
(void)buffer; (void)idx; (void)maxlen; | |||||
if (character) { | |||||
_putchar(character); | |||||
} | |||||
} | |||||
// internal output function wrapper | |||||
static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) | |||||
{ | |||||
(void)idx; (void)maxlen; | |||||
if (character) { | |||||
// buffer is the output fct pointer | |||||
((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg); | |||||
} | |||||
} | |||||
// internal secure strlen | |||||
// \return The length of the string (excluding the terminating 0) limited by 'maxsize' | |||||
static inline unsigned int _strnlen_s(const char* str, size_t maxsize) | |||||
{ | |||||
const char* s; | |||||
for (s = str; *s && maxsize--; ++s); | |||||
return (unsigned int)(s - str); | |||||
} | |||||
// internal test if char is a digit (0-9) | |||||
// \return true if char is a digit | |||||
static inline bool _is_digit(char ch) | |||||
{ | |||||
return (ch >= '0') && (ch <= '9'); | |||||
} | |||||
// internal ASCII string to unsigned int conversion | |||||
static unsigned int _atoi(const char** str) | |||||
{ | |||||
unsigned int i = 0U; | |||||
while (_is_digit(**str)) { | |||||
i = i * 10U + (unsigned int)(*((*str)++) - '0'); | |||||
} | |||||
return i; | |||||
} | |||||
// output the specified string in reverse, taking care of any zero-padding | |||||
static size_t _out_rev(out_fct_type out, char* buffer, size_t idx, size_t maxlen, const char* buf, size_t len, unsigned int width, unsigned int flags) | |||||
{ | |||||
const size_t start_idx = idx; | |||||
// pad spaces up to given width | |||||
if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) { | |||||
for (size_t i = len; i < width; i++) { | |||||
out(' ', buffer, idx++, maxlen); | |||||
} | |||||
} | |||||
// reverse string | |||||
while (len) { | |||||
out(buf[--len], buffer, idx++, maxlen); | |||||
} | |||||
// append pad spaces up to given width | |||||
if (flags & FLAGS_LEFT) { | |||||
while (idx - start_idx < width) { | |||||
out(' ', buffer, idx++, maxlen); | |||||
} | |||||
} | |||||
return idx; | |||||
} | |||||
// internal itoa format | |||||
static size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags) | |||||
{ | |||||
// pad leading zeros | |||||
if (!(flags & FLAGS_LEFT)) { | |||||
if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { | |||||
width--; | |||||
} | |||||
while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) { | |||||
buf[len++] = '0'; | |||||
} | |||||
while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) { | |||||
buf[len++] = '0'; | |||||
} | |||||
} | |||||
// handle hash | |||||
if (flags & FLAGS_HASH) { | |||||
if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) { | |||||
len--; | |||||
if (len && (base == 16U)) { | |||||
len--; | |||||
} | |||||
} | |||||
if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { | |||||
buf[len++] = 'x'; | |||||
} | |||||
else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) { | |||||
buf[len++] = 'X'; | |||||
} | |||||
else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) { | |||||
buf[len++] = 'b'; | |||||
} | |||||
if (len < PRINTF_NTOA_BUFFER_SIZE) { | |||||
buf[len++] = '0'; | |||||
} | |||||
} | |||||
if (len < PRINTF_NTOA_BUFFER_SIZE) { | |||||
if (negative) { | |||||
buf[len++] = '-'; | |||||
} | |||||
else if (flags & FLAGS_PLUS) { | |||||
buf[len++] = '+'; // ignore the space if the '+' exists | |||||
} | |||||
else if (flags & FLAGS_SPACE) { | |||||
buf[len++] = ' '; | |||||
} | |||||
} | |||||
return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); | |||||
} | |||||
// internal itoa for 'long' type | |||||
static size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) | |||||
{ | |||||
char buf[PRINTF_NTOA_BUFFER_SIZE]; | |||||
size_t len = 0U; | |||||
int q, r; | |||||
// no hash for 0 values | |||||
if (!value) { | |||||
flags &= ~FLAGS_HASH; | |||||
} | |||||
// write if precision != 0 and value is != 0 | |||||
if (!(flags & FLAGS_PRECISION) || value) { | |||||
q = 0, r = 0; | |||||
len = 0; | |||||
do { | |||||
divmod(&q, &r, value, base); | |||||
const char digit = (char)(r); | |||||
// buf[idx2++] = '0' + digit; | |||||
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; | |||||
value = q; | |||||
} while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); | |||||
// while (idx2 > 0) { | |||||
// _putchar(buf[--idx2]); | |||||
// written++; | |||||
// } | |||||
// idx++; | |||||
// do { | |||||
// const char digit = (char)(value % base); | |||||
// buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; | |||||
// value /= base; | |||||
// } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); | |||||
} | |||||
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); | |||||
} | |||||
// // internal itoa for 'long long' type | |||||
#if defined(PRINTF_SUPPORT_LONG_LONG) | |||||
static size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) | |||||
{ | |||||
char buf[PRINTF_NTOA_BUFFER_SIZE]; | |||||
size_t len = 0U; | |||||
long long q, r; | |||||
// no hash for 0 values | |||||
if (!value) { | |||||
flags &= ~FLAGS_HASH; | |||||
} | |||||
// write if precision != 0 and value is != 0 | |||||
if (!(flags & FLAGS_PRECISION) || value) { | |||||
q = 0, r = 0; | |||||
len = 0; | |||||
do { | |||||
divmod_long_long(&q, &r, value, base); | |||||
const char digit = (char)(r); | |||||
// buf[idx2++] = '0' + digit; | |||||
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; | |||||
value = q; | |||||
} while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); | |||||
// do { | |||||
// const char digit = (char)(value % base); | |||||
// buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10; | |||||
// value /= base; | |||||
// } while (value && (len < PRINTF_NTOA_BUFFER_SIZE)); | |||||
} | |||||
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags); | |||||
} | |||||
#endif // PRINTF_SUPPORT_LONG_LONG | |||||
// #if defined(PRINTF_SUPPORT_FLOAT) | |||||
// | |||||
// #if defined(PRINTF_SUPPORT_EXPONENTIAL) | |||||
// // forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT | |||||
// static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags); | |||||
// #endif | |||||
// | |||||
// | |||||
// // internal ftoa for fixed decimal floating point | |||||
// static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) | |||||
// { | |||||
// char buf[PRINTF_FTOA_BUFFER_SIZE]; | |||||
// size_t len = 0U; | |||||
// double diff = 0.0; | |||||
// | |||||
// // powers of 10 | |||||
// static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 }; | |||||
// | |||||
// // test for special values | |||||
// if (value != value) | |||||
// return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags); | |||||
// if (value < -DBL_MAX) | |||||
// return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags); | |||||
// if (value > DBL_MAX) | |||||
// return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags); | |||||
// | |||||
// // test for very large values | |||||
// // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad | |||||
// if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) { | |||||
// #if defined(PRINTF_SUPPORT_EXPONENTIAL) | |||||
// return _etoa(out, buffer, idx, maxlen, value, prec, width, flags); | |||||
// #else | |||||
// return 0U; | |||||
// #endif | |||||
// } | |||||
// | |||||
// // test for negative | |||||
// bool negative = false; | |||||
// if (value < 0) { | |||||
// negative = true; | |||||
// value = 0 - value; | |||||
// } | |||||
// | |||||
// // set default precision, if not set explicitly | |||||
// if (!(flags & FLAGS_PRECISION)) { | |||||
// prec = PRINTF_DEFAULT_FLOAT_PRECISION; | |||||
// } | |||||
// // limit precision to 9, cause a prec >= 10 can lead to overflow errors | |||||
// while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) { | |||||
// buf[len++] = '0'; | |||||
// prec--; | |||||
// } | |||||
// | |||||
// int whole = (int)value; | |||||
// double tmp = (value - whole) * pow10[prec]; | |||||
// unsigned long frac = (unsigned long)tmp; | |||||
// diff = tmp - frac; | |||||
// | |||||
// if (diff > 0.5) { | |||||
// ++frac; | |||||
// // handle rollover, e.g. case 0.99 with prec 1 is 1.0 | |||||
// if (frac >= pow10[prec]) { | |||||
// frac = 0; | |||||
// ++whole; | |||||
// } | |||||
// } | |||||
// else if (diff < 0.5) { | |||||
// } | |||||
// else if ((frac == 0U) || (frac & 1U)) { | |||||
// // if halfway, round up if odd OR if last digit is 0 | |||||
// ++frac; | |||||
// } | |||||
// | |||||
// if (prec == 0U) { | |||||
// diff = value - (double)whole; | |||||
// if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) { | |||||
// // exactly 0.5 and ODD, then round up | |||||
// // 1.5 -> 2, but 2.5 -> 2 | |||||
// ++whole; | |||||
// } | |||||
// } | |||||
// else { | |||||
// unsigned int count = prec; | |||||
// // now do fractional part, as an unsigned number | |||||
// while (len < PRINTF_FTOA_BUFFER_SIZE) { | |||||
// --count; | |||||
// buf[len++] = (char)(48U + (frac % 10U)); | |||||
// if (!(frac /= 10U)) { | |||||
// break; | |||||
// } | |||||
// } | |||||
// // add extra 0s | |||||
// while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) { | |||||
// buf[len++] = '0'; | |||||
// } | |||||
// if (len < PRINTF_FTOA_BUFFER_SIZE) { | |||||
// // add decimal | |||||
// buf[len++] = '.'; | |||||
// } | |||||
// } | |||||
// | |||||
// // do whole part, number is reversed | |||||
// while (len < PRINTF_FTOA_BUFFER_SIZE) { | |||||
// buf[len++] = (char)(48 + (whole % 10)); | |||||
// if (!(whole /= 10)) { | |||||
// break; | |||||
// } | |||||
// } | |||||
// | |||||
// // pad leading zeros | |||||
// if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) { | |||||
// if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) { | |||||
// width--; | |||||
// } | |||||
// while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) { | |||||
// buf[len++] = '0'; | |||||
// } | |||||
// } | |||||
// | |||||
// if (len < PRINTF_FTOA_BUFFER_SIZE) { | |||||
// if (negative) { | |||||
// buf[len++] = '-'; | |||||
// } | |||||
// else if (flags & FLAGS_PLUS) { | |||||
// buf[len++] = '+'; // ignore the space if the '+' exists | |||||
// } | |||||
// else if (flags & FLAGS_SPACE) { | |||||
// buf[len++] = ' '; | |||||
// } | |||||
// } | |||||
// | |||||
// return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags); | |||||
// } | |||||
// | |||||
// | |||||
// #if defined(PRINTF_SUPPORT_EXPONENTIAL) | |||||
// // internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com> | |||||
// static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags) | |||||
// { | |||||
// // check for NaN and special values | |||||
// if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) { | |||||
// return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags); | |||||
// } | |||||
// | |||||
// // determine the sign | |||||
// const bool negative = value < 0; | |||||
// if (negative) { | |||||
// value = -value; | |||||
// } | |||||
// | |||||
// // default precision | |||||
// if (!(flags & FLAGS_PRECISION)) { | |||||
// prec = PRINTF_DEFAULT_FLOAT_PRECISION; | |||||
// } | |||||
// | |||||
// // determine the decimal exponent | |||||
// // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c) | |||||
// union { | |||||
// uint64_t U; | |||||
// double F; | |||||
// } conv; | |||||
// | |||||
// conv.F = value; | |||||
// int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2 | |||||
// conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2) | |||||
// // now approximate log10 from the log2 integer part and an expansion of ln around 1.5 | |||||
// int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168); | |||||
// // now we want to compute 10^expval but we want to be sure it won't overflow | |||||
// exp2 = (int)(expval * 3.321928094887362 + 0.5); | |||||
// const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453; | |||||
// const double z2 = z * z; | |||||
// conv.U = (uint64_t)(exp2 + 1023) << 52U; | |||||
// // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex | |||||
// conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14))))); | |||||
// // correct for rounding errors | |||||
// if (value < conv.F) { | |||||
// expval--; | |||||
// conv.F /= 10; | |||||
// } | |||||
// | |||||
// // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters | |||||
// unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U; | |||||
// | |||||
// // in "%g" mode, "prec" is the number of *significant figures* not decimals | |||||
// if (flags & FLAGS_ADAPT_EXP) { | |||||
// // do we want to fall-back to "%f" mode? | |||||
// if ((value >= 1e-4) && (value < 1e6)) { | |||||
// if ((int)prec > expval) { | |||||
// prec = (unsigned)((int)prec - expval - 1); | |||||
// } | |||||
// else { | |||||
// prec = 0; | |||||
// } | |||||
// flags |= FLAGS_PRECISION; // make sure _ftoa respects precision | |||||
// // no characters in exponent | |||||
// minwidth = 0U; | |||||
// expval = 0; | |||||
// } | |||||
// else { | |||||
// // we use one sigfig for the whole part | |||||
// if ((prec > 0) && (flags & FLAGS_PRECISION)) { | |||||
// --prec; | |||||
// } | |||||
// } | |||||
// } | |||||
// | |||||
// // will everything fit? | |||||
// unsigned int fwidth = width; | |||||
// if (width > minwidth) { | |||||
// // we didn't fall-back so subtract the characters required for the exponent | |||||
// fwidth -= minwidth; | |||||
// } else { | |||||
// // not enough characters, so go back to default sizing | |||||
// fwidth = 0U; | |||||
// } | |||||
// if ((flags & FLAGS_LEFT) && minwidth) { | |||||
// // if we're padding on the right, DON'T pad the floating part | |||||
// fwidth = 0U; | |||||
// } | |||||
// | |||||
// // rescale the float value | |||||
// if (expval) { | |||||
// value /= conv.F; | |||||
// } | |||||
// | |||||
// // output the floating part | |||||
// const size_t start_idx = idx; | |||||
// idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP); | |||||
// | |||||
// // output the exponent part | |||||
// if (minwidth) { | |||||
// // output the exponential symbol | |||||
// out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen); | |||||
// // output the exponent value | |||||
// idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS); | |||||
// // might need to right-pad spaces | |||||
// if (flags & FLAGS_LEFT) { | |||||
// while (idx - start_idx < width) out(' ', buffer, idx++, maxlen); | |||||
// } | |||||
// } | |||||
// return idx; | |||||
// } | |||||
// #endif // PRINTF_SUPPORT_EXPONENTIAL | |||||
// #endif // PRINTF_SUPPORT_FLOAT | |||||
// internal vsnprintf | |||||
static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va) | |||||
{ | |||||
unsigned int flags, width, precision, n; | |||||
size_t idx = 0U; | |||||
if (!buffer) { | |||||
// use null output function | |||||
out = _out_null; | |||||
} | |||||
while (*format) | |||||
{ | |||||
// format specifier? %[flags][width][.precision][length] | |||||
if (*format != '%') { | |||||
// no | |||||
out(*format, buffer, idx++, maxlen); | |||||
format++; | |||||
continue; | |||||
} | |||||
else { | |||||
// yes, evaluate it | |||||
format++; | |||||
} | |||||
// evaluate flags | |||||
flags = 0U; | |||||
do { | |||||
switch (*format) { | |||||
case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break; | |||||
case '-': flags |= FLAGS_LEFT; format++; n = 1U; break; | |||||
case '+': flags |= FLAGS_PLUS; format++; n = 1U; break; | |||||
case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break; | |||||
case '#': flags |= FLAGS_HASH; format++; n = 1U; break; | |||||
default : n = 0U; break; | |||||
} | |||||
} while (n); | |||||
// evaluate width field | |||||
width = 0U; | |||||
if (_is_digit(*format)) { | |||||
width = _atoi(&format); | |||||
} | |||||
else if (*format == '*') { | |||||
const int w = va_arg(va, int); | |||||
if (w < 0) { | |||||
flags |= FLAGS_LEFT; // reverse padding | |||||
width = (unsigned int)-w; | |||||
} | |||||
else { | |||||
width = (unsigned int)w; | |||||
} | |||||
format++; | |||||
} | |||||
// evaluate precision field | |||||
precision = 0U; | |||||
if (*format == '.') { | |||||
flags |= FLAGS_PRECISION; | |||||
format++; | |||||
if (_is_digit(*format)) { | |||||
precision = _atoi(&format); | |||||
} | |||||
else if (*format == '*') { | |||||
const int prec = (int)va_arg(va, int); | |||||
precision = prec > 0 ? (unsigned int)prec : 0U; | |||||
format++; | |||||
} | |||||
} | |||||
// evaluate length field | |||||
switch (*format) { | |||||
case 'l' : | |||||
flags |= FLAGS_LONG; | |||||
format++; | |||||
if (*format == 'l') { | |||||
flags |= FLAGS_LONG_LONG; | |||||
format++; | |||||
} | |||||
break; | |||||
case 'h' : | |||||
flags |= FLAGS_SHORT; | |||||
format++; | |||||
if (*format == 'h') { | |||||
flags |= FLAGS_CHAR; | |||||
format++; | |||||
} | |||||
break; | |||||
// #if defined(PRINTF_SUPPORT_PTRDIFF_T) | |||||
// case 't' : | |||||
// flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); | |||||
// format++; | |||||
// break; | |||||
// #endif | |||||
case 'j' : | |||||
flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); | |||||
format++; | |||||
break; | |||||
case 'z' : | |||||
flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG); | |||||
format++; | |||||
break; | |||||
default : | |||||
break; | |||||
} | |||||
// evaluate specifier | |||||
switch (*format) { | |||||
case 'd' : | |||||
case 'i' : | |||||
case 'u' : | |||||
case 'x' : | |||||
case 'X' : | |||||
case 'o' : | |||||
case 'b' : { | |||||
// set the base | |||||
unsigned int base; | |||||
if (*format == 'x' || *format == 'X') { | |||||
base = 16U; | |||||
} | |||||
else if (*format == 'o') { | |||||
base = 8U; | |||||
} | |||||
else if (*format == 'b') { | |||||
base = 2U; | |||||
} | |||||
else { | |||||
base = 10U; | |||||
flags &= ~FLAGS_HASH; // no hash for dec format | |||||
} | |||||
// uppercase | |||||
if (*format == 'X') { | |||||
flags |= FLAGS_UPPERCASE; | |||||
} | |||||
// no plus or space flag for u, x, X, o, b | |||||
if ((*format != 'i') && (*format != 'd')) { | |||||
flags &= ~(FLAGS_PLUS | FLAGS_SPACE); | |||||
} | |||||
// ignore '0' flag when precision is given | |||||
if (flags & FLAGS_PRECISION) { | |||||
flags &= ~FLAGS_ZEROPAD; | |||||
} | |||||
// convert the integer | |||||
if ((*format == 'i') || (*format == 'd')) { | |||||
// signed | |||||
if (flags & FLAGS_LONG_LONG) { | |||||
#if defined(PRINTF_SUPPORT_LONG_LONG) | |||||
const long long value = va_arg(va, long long); | |||||
idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); | |||||
#endif | |||||
} | |||||
else if (flags & FLAGS_LONG) { | |||||
const long value = va_arg(va, long); | |||||
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); | |||||
} | |||||
else { | |||||
const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int); | |||||
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags); | |||||
} | |||||
} | |||||
else { | |||||
// unsigned | |||||
if (flags & FLAGS_LONG_LONG) { | |||||
#if defined(PRINTF_SUPPORT_LONG_LONG) | |||||
idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags); | |||||
#endif | |||||
} | |||||
else if (flags & FLAGS_LONG) { | |||||
idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags); | |||||
} | |||||
else { | |||||
const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int); | |||||
idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags); | |||||
} | |||||
} | |||||
format++; | |||||
break; | |||||
} | |||||
// #if defined(PRINTF_SUPPORT_FLOAT) | |||||
// case 'f' : | |||||
// case 'F' : | |||||
// if (*format == 'F') flags |= FLAGS_UPPERCASE; | |||||
// idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); | |||||
// format++; | |||||
// break; | |||||
// #if defined(PRINTF_SUPPORT_EXPONENTIAL) | |||||
// case 'e': | |||||
// case 'E': | |||||
// case 'g': | |||||
// case 'G': | |||||
// if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP; | |||||
// if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE; | |||||
// idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags); | |||||
// format++; | |||||
// break; | |||||
// #endif // PRINTF_SUPPORT_EXPONENTIAL | |||||
// #endif // PRINTF_SUPPORT_FLOAT | |||||
case 'c' : { | |||||
unsigned int l = 1U; | |||||
// pre padding | |||||
if (!(flags & FLAGS_LEFT)) { | |||||
while (l++ < width) { | |||||
out(' ', buffer, idx++, maxlen); | |||||
} | |||||
} | |||||
// char output | |||||
out((char)va_arg(va, int), buffer, idx++, maxlen); | |||||
// post padding | |||||
if (flags & FLAGS_LEFT) { | |||||
while (l++ < width) { | |||||
out(' ', buffer, idx++, maxlen); | |||||
} | |||||
} | |||||
format++; | |||||
break; | |||||
} | |||||
case 's' : { | |||||
const char* p = va_arg(va, char*); | |||||
unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1); | |||||
// pre padding | |||||
if (flags & FLAGS_PRECISION) { | |||||
l = (l < precision ? l : precision); | |||||
} | |||||
if (!(flags & FLAGS_LEFT)) { | |||||
while (l++ < width) { | |||||
out(' ', buffer, idx++, maxlen); | |||||
} | |||||
} | |||||
// string output | |||||
while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) { | |||||
out(*(p++), buffer, idx++, maxlen); | |||||
} | |||||
// post padding | |||||
if (flags & FLAGS_LEFT) { | |||||
while (l++ < width) { | |||||
out(' ', buffer, idx++, maxlen); | |||||
} | |||||
} | |||||
format++; | |||||
break; | |||||
} | |||||
case 'p' : { | |||||
width = sizeof(void*) * 2U; | |||||
flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE; | |||||
#if defined(PRINTF_SUPPORT_LONG_LONG) | |||||
const bool is_ll = sizeof(uintptr_t) == sizeof(long long); | |||||
if (is_ll) { | |||||
idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags); | |||||
} | |||||
else { | |||||
#endif | |||||
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags); | |||||
#if defined(PRINTF_SUPPORT_LONG_LONG) | |||||
} | |||||
#endif | |||||
format++; | |||||
break; | |||||
} | |||||
case '%' : | |||||
out('%', buffer, idx++, maxlen); | |||||
format++; | |||||
break; | |||||
default : | |||||
out(*format, buffer, idx++, maxlen); | |||||
format++; | |||||
break; | |||||
} | |||||
} | |||||
// termination | |||||
out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen); | |||||
// return written chars without terminating \0 | |||||
return (int)idx; | |||||
} | |||||
/////////////////////////////////////////////////////////////////////////////// | |||||
int printf_(const char* format, ...) | |||||
{ | |||||
va_list va; | |||||
va_start(va, format); | |||||
char buffer[1]; | |||||
const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va); | |||||
va_end(va); | |||||
return ret; | |||||
} | |||||
int sprintf_(char* buffer, const char* format, ...) | |||||
{ | |||||
va_list va; | |||||
va_start(va, format); | |||||
const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va); | |||||
va_end(va); | |||||
return ret; | |||||
} | |||||
int snprintf_(char* buffer, size_t count, const char* format, ...) | |||||
{ | |||||
va_list va; | |||||
va_start(va, format); | |||||
const int ret = _vsnprintf(_out_buffer, buffer, count, format, va); | |||||
va_end(va); | |||||
return ret; | |||||
} | |||||
int vprintf_(const char* format, va_list va) | |||||
{ | |||||
char buffer[1]; | |||||
return _vsnprintf(_out_char, buffer, (size_t)-1, format, va); | |||||
} | |||||
int vsnprintf_(char* buffer, size_t count, const char* format, va_list va) | |||||
{ | |||||
return _vsnprintf(_out_buffer, buffer, count, format, va); | |||||
} | |||||
int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...) | |||||
{ | |||||
va_list va; | |||||
va_start(va, format); | |||||
const out_fct_wrap_type out_fct_wrap = { out, arg }; | |||||
const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va); | |||||
va_end(va); | |||||
return ret; | |||||
} |
@ -0,0 +1,32 @@ | |||||
TOPMOD := PQVexRiscvUlx3s | |||||
CHIP := 85k | |||||
PACKAGE := CABGA381 | |||||
CONSTRAINTS := ulx3s_v20_constraints | |||||
VLOGFIL := $(TOPMOD).v | |||||
PROGPATH := ~/bin/ujprog/ujprog | |||||
.PHONY: all | |||||
all: $(TOPMOD).bit | |||||
.PHONY: clean | |||||
clean: | |||||
rm -rf $(TOPMOD).json $(TOPMOD).config $(TOPMOD).bit $(TOPMOD)*.bin | |||||
$(TOPMOD).bit: $(TOPMOD).config | |||||
ecppack $(TOPMOD).config $(TOPMOD).bit | |||||
$(TOPMOD).config: $(TOPMOD).json | |||||
nextpnr-ecp5 \ | |||||
--$(CHIP) \ | |||||
--package $(PACKAGE) \ | |||||
--json $(TOPMOD).json \ | |||||
--lpf-allow-unconstrained \ | |||||
--lpf $(CONSTRAINTS).lpf \ | |||||
--textcfg $(TOPMOD).config | |||||
$(TOPMOD).json: $(TOPMOD).v | |||||
yosys -q -l $(TOPMOD)_yosys.log -p "synth_ecp5 -top $(TOPMOD) -json $(TOPMOD).json" $(TOPMOD).v | |||||
prog: $(TOPMOD).bit | |||||
sudo $(PROGPATH) $(TOPMOD).bit |
@ -0,0 +1,2 @@ | |||||
read_verilog PQVexRiscvUlx3s.v | |||||
synth_ecp5 -json PQVexRiscvUlx3s.json |
@ -0,0 +1,452 @@ | |||||
BLOCK RESETPATHS; | |||||
BLOCK ASYNCPATHS; | |||||
## ULX3S v2.x.x and v3.0.x | |||||
# The clock "usb" and "gpdi" sheet | |||||
LOCATE COMP "io_mainClock" SITE "G2"; | |||||
IOBUF PORT "io_mainClock" PULLMODE=NONE IO_TYPE=LVCMOS33; | |||||
FREQUENCY PORT "io_mainClock" 25 MHZ; | |||||
# JTAG and SPI FLASH voltage 3.3V and options to boot from SPI flash | |||||
# write to FLASH possible any time from JTAG: | |||||
# SYSCONFIG CONFIG_IOVOLTAGE=3.3 COMPRESS_CONFIG=ON MCCLK_FREQ=62 MASTER_SPI_PORT=ENABLE SLAVE_SPI_PORT=DISABLE SLAVE_PARALLEL_PORT=DISABLE; | |||||
# write to FLASH possible from user bitstream: | |||||
# SYSCONFIG CONFIG_IOVOLTAGE=3.3 COMPRESS_CONFIG=ON MCCLK_FREQ=62 MASTER_SPI_PORT=DISABLE SLAVE_SPI_PORT=DISABLE SLAVE_PARALLEL_PORT=DISABLE; | |||||
## USBSERIAL FTDI-FPGA serial port "usb" sheet | |||||
LOCATE COMP "io_uart_txd" SITE "L4"; # FPGA transmits to ftdi | |||||
LOCATE COMP "io_uart_rxd" SITE "M1"; # FPGA receives from ftdi | |||||
LOCATE COMP "ftdi_nrts" SITE "M3"; # FPGA receives | |||||
LOCATE COMP "ftdi_ndtr" SITE "N1"; # FPGA receives | |||||
LOCATE COMP "ftdi_txden" SITE "L3"; # FPGA receives | |||||
IOBUF PORT "io_uart_txd" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "io_uart_rxd" PULLMODE=UP IO_TYPE=LVCMOS33; | |||||
IOBUF PORT "ftdi_nrts" PULLMODE=UP IO_TYPE=LVCMOS33; | |||||
IOBUF PORT "ftdi_ndtr" PULLMODE=UP IO_TYPE=LVCMOS33; | |||||
IOBUF PORT "ftdi_txden" PULLMODE=UP IO_TYPE=LVCMOS33; | |||||
## LED indicators "blinkey" and "gpio" sheet | |||||
LOCATE COMP "led[7]" SITE "H3"; | |||||
LOCATE COMP "led[6]" SITE "E1"; | |||||
LOCATE COMP "led[5]" SITE "E2"; | |||||
LOCATE COMP "led[4]" SITE "D1"; | |||||
LOCATE COMP "led[3]" SITE "D2"; | |||||
LOCATE COMP "led[2]" SITE "C1"; | |||||
LOCATE COMP "led[1]" SITE "C2"; | |||||
LOCATE COMP "led[0]" SITE "B2"; | |||||
IOBUF PORT "led[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "led[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "led[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "led[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "led[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "led[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "led[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "led[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
## Pushbuttons "blinkey", "flash", "power", "gpdi" sheet | |||||
LOCATE COMP "btn[0]" SITE "D6"; # BTN_PWRn (inverted logic) | |||||
LOCATE COMP "io_asyncReset" SITE "R1"; # FIRE1 | |||||
LOCATE COMP "btn[2]" SITE "T1"; # FIRE2 | |||||
LOCATE COMP "btn[3]" SITE "R18"; # UP W1->R18 | |||||
LOCATE COMP "btn[4]" SITE "V1"; # DOWN | |||||
LOCATE COMP "btn[5]" SITE "U1"; # LEFT | |||||
LOCATE COMP "btn[6]" SITE "H16"; # RIGHT Y2->H16 | |||||
IOBUF PORT "btn[0]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "io_asyncReset" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "btn[2]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "btn[3]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "btn[4]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "btn[5]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "btn[6]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; | |||||
## DIP switch "blinkey", "gpio" sheet | |||||
LOCATE COMP "sw[0]" SITE "E8"; # SW1 | |||||
LOCATE COMP "sw[1]" SITE "D8"; # SW2 | |||||
LOCATE COMP "sw[2]" SITE "D7"; # SW3 | |||||
LOCATE COMP "sw[3]" SITE "E7"; # SW4 | |||||
IOBUF PORT "sw[0]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sw[1]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sw[2]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sw[3]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; | |||||
## SPI OLED DISPLAY SSD1331 (Color) or SSD1306 (B/W) "blinkey", "usb" sheet | |||||
LOCATE COMP "oled_clk" SITE "P4"; | |||||
LOCATE COMP "oled_mosi" SITE "P3"; | |||||
LOCATE COMP "oled_dc" SITE "P1"; | |||||
LOCATE COMP "oled_resn" SITE "P2"; | |||||
LOCATE COMP "oled_csn" SITE "N2"; | |||||
IOBUF PORT "oled_clk" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "oled_mosi" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "oled_dc" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "oled_resn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "oled_csn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
## SPI Flash chip "flash" sheet | |||||
LOCATE COMP "flash_csn" SITE "R2"; | |||||
LOCATE COMP "flash_clk" SITE "U3"; | |||||
LOCATE COMP "flash_mosi" SITE "W2"; | |||||
LOCATE COMP "flash_miso" SITE "V2"; | |||||
LOCATE COMP "flash_holdn" SITE "W1"; | |||||
LOCATE COMP "flash_wpn" SITE "Y2"; | |||||
#LOCATE COMP "flash_csspin" SITE "AJ3"; | |||||
#LOCATE COMP "flash_initn" SITE "AG4"; | |||||
#LOCATE COMP "flash_done" SITE "AJ4"; | |||||
#LOCATE COMP "flash_programn" SITE "AH4"; | |||||
#LOCATE COMP "flash_cfg_select[0]" SITE "AM4"; | |||||
#LOCATE COMP "flash_cfg_select[1]" SITE "AL4"; | |||||
#LOCATE COMP "flash_cfg_select[2]" SITE "AK4"; | |||||
IOBUF PORT "flash_csn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "flash_clk" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "flash_mosi" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "flash_miso" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "flash_holdn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "flash_wpn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
#IOBUF PORT "flash_csspin" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
#IOBUF PORT "flash_initn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
#IOBUF PORT "flash_done" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
#IOBUF PORT "flash_programn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
#IOBUF PORT "flash_cfg_select[0]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; | |||||
#IOBUF PORT "flash_cfg_select[1]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; | |||||
#IOBUF PORT "flash_cfg_select[2]" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; | |||||
## SD card "sdcard", "usb" sheet | |||||
LOCATE COMP "sd_clk" SITE "H2"; # sd_clk WiFi_GPIO14 | |||||
LOCATE COMP "sd_cmd" SITE "J1"; # sd_cmd_di (MOSI) WiFi GPIO15 | |||||
LOCATE COMP "sd_d[0]" SITE "J3"; # sd_dat0_do (MISO) WiFi GPIO2 | |||||
LOCATE COMP "sd_d[1]" SITE "H1"; # sd_dat1_irq WiFi GPIO4 | |||||
LOCATE COMP "sd_d[2]" SITE "K1"; # sd_dat2 WiFi_GPIO12 | |||||
LOCATE COMP "sd_d[3]" SITE "K2"; # sd_dat3_csn WiFi_GPIO13 | |||||
LOCATE COMP "sd_wp" SITE "P5"; # not connected | |||||
LOCATE COMP "sd_cdn" SITE "N5"; # not connected | |||||
IOBUF PORT "sd_clk" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sd_cmd" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sd_d[0]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sd_d[1]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sd_d[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; # WiFi GPIO12 pulldown bootstrapping requirement | |||||
IOBUF PORT "sd_d[3]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sd_wp" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sd_cdn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
## ADC SPI (MAX11123) "analog", "ram" sheet | |||||
LOCATE COMP "adc_csn" SITE "R17"; | |||||
LOCATE COMP "adc_mosi" SITE "R16"; | |||||
LOCATE COMP "adc_miso" SITE "U16"; | |||||
LOCATE COMP "adc_sclk" SITE "P17"; | |||||
IOBUF PORT "adc_csn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "adc_mosi" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "adc_miso" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "adc_sclk" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
## Audio 4-bit DAC "analog", "gpio" sheet | |||||
# Output impedance 75 ohm. | |||||
# Strong enough to drive 16 ohm earphones. | |||||
LOCATE COMP "audio_l[3]" SITE "B3"; # JACK TIP (left audio) | |||||
LOCATE COMP "audio_l[2]" SITE "C3"; | |||||
LOCATE COMP "audio_l[1]" SITE "D3"; | |||||
LOCATE COMP "audio_l[0]" SITE "E4"; | |||||
LOCATE COMP "audio_r[3]" SITE "C5"; # JACK RING1 (right audio) | |||||
LOCATE COMP "audio_r[2]" SITE "D5"; | |||||
LOCATE COMP "audio_r[1]" SITE "B5"; | |||||
LOCATE COMP "audio_r[0]" SITE "A3"; | |||||
LOCATE COMP "audio_v[3]" SITE "E5"; # JACK RING2 (video or digital audio) | |||||
LOCATE COMP "audio_v[2]" SITE "F5"; | |||||
LOCATE COMP "audio_v[1]" SITE "F2"; | |||||
LOCATE COMP "audio_v[0]" SITE "H5"; | |||||
IOBUF PORT "audio_l[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; | |||||
IOBUF PORT "audio_l[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; | |||||
IOBUF PORT "audio_l[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; | |||||
IOBUF PORT "audio_l[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; | |||||
IOBUF PORT "audio_r[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; | |||||
IOBUF PORT "audio_r[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; | |||||
IOBUF PORT "audio_r[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; | |||||
IOBUF PORT "audio_r[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; | |||||
IOBUF PORT "audio_v[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; | |||||
IOBUF PORT "audio_v[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; | |||||
IOBUF PORT "audio_v[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; | |||||
IOBUF PORT "audio_v[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; | |||||
## WiFi ESP-32 "wifi", "usb", "flash" sheet | |||||
# other pins are shared with GP/GN, SD card and JTAG | |||||
LOCATE COMP "wifi_en" SITE "F1"; # enable/reset WiFi | |||||
LOCATE COMP "wifi_rxd" SITE "K3"; # FPGA transmits to WiFi | |||||
LOCATE COMP "wifi_txd" SITE "K4"; # FPGA receives from WiFi | |||||
LOCATE COMP "wifi_gpio0" SITE "L2"; | |||||
LOCATE COMP "wifi_gpio5" SITE "N4"; # WIFI LED | |||||
LOCATE COMP "wifi_gpio16" SITE "L1"; # Serial1 RX | |||||
LOCATE COMP "wifi_gpio17" SITE "N3"; # Serial1 TX | |||||
# LOCATE COMP "prog_done" SITE "Y3"; # not GPIO, always active | |||||
IOBUF PORT "wifi_en" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "wifi_rxd" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "wifi_txd" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "wifi_gpio0" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "wifi_gpio16" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "wifi_gpio17" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
# IOBUF PORT "prog_done" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
## PCB antenna 433 MHz (may be also used for FM) "usb" sheet | |||||
LOCATE COMP "ant_433mhz" SITE "G1"; | |||||
IOBUF PORT "ant_433mhz" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
## Second USB port "US2" going directly into FPGA "usb", "ram" sheet | |||||
LOCATE COMP "usb_fpga_dp" SITE "E16"; # single ended or differential input only | |||||
LOCATE COMP "usb_fpga_dn" SITE "F16"; | |||||
IOBUF PORT "usb_fpga_dp" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; | |||||
IOBUF PORT "usb_fpga_dn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; | |||||
LOCATE COMP "usb_fpga_bd_dp" SITE "D15"; # differential bidirectional | |||||
LOCATE COMP "usb_fpga_bd_dn" SITE "E15"; | |||||
IOBUF PORT "usb_fpga_bd_dp" PULLMODE=NONE IO_TYPE=LVCMOS33D DRIVE=4; | |||||
IOBUF PORT "usb_fpga_bd_dn" PULLMODE=NONE IO_TYPE=LVCMOS33D DRIVE=4; | |||||
LOCATE COMP "usb_fpga_pu_dp" SITE "B12"; # pull up/down control | |||||
LOCATE COMP "usb_fpga_pu_dn" SITE "C12"; | |||||
IOBUF PORT "usb_fpga_pu_dp" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; | |||||
IOBUF PORT "usb_fpga_pu_dn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=16; | |||||
## JTAG ESP-32 "usb" sheet | |||||
# connected to FT231X and ESP-32 | |||||
# commented out because those are dedicated pins, not directly useable as GPIO | |||||
# but could be used by some vendor-specific JTAG bridging (boundary scan) module | |||||
#LOCATE COMP "jtag_tdi" SITE "R5"; # FTDI_nRI FPGA receives | |||||
#LOCATE COMP "jtag_tdo" SITE "V4"; # FTDI_nCTS FPGA transmits | |||||
#LOCATE COMP "jtag_tck" SITE "T5"; # FTDI_nDSR FPGA receives | |||||
#LOCATE COMP "jtag_tms" SITE "U5"; # FTDI_nDCD FPGA receives | |||||
#IOBUF PORT "jtag_tdi" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
#IOBUF PORT "jtag_tdo" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
#IOBUF PORT "jtag_tck" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
#IOBUF PORT "jtag_tms" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
## SDRAM "ram" sheet | |||||
LOCATE COMP "sdram_clk" SITE "F19"; | |||||
LOCATE COMP "sdram_cke" SITE "F20"; | |||||
LOCATE COMP "sdram_csn" SITE "P20"; | |||||
LOCATE COMP "sdram_wen" SITE "T20"; | |||||
LOCATE COMP "sdram_rasn" SITE "R20"; | |||||
LOCATE COMP "sdram_casn" SITE "T19"; | |||||
LOCATE COMP "sdram_a[0]" SITE "M20"; | |||||
LOCATE COMP "sdram_a[1]" SITE "M19"; | |||||
LOCATE COMP "sdram_a[2]" SITE "L20"; | |||||
LOCATE COMP "sdram_a[3]" SITE "L19"; | |||||
LOCATE COMP "sdram_a[4]" SITE "K20"; | |||||
LOCATE COMP "sdram_a[5]" SITE "K19"; | |||||
LOCATE COMP "sdram_a[6]" SITE "K18"; | |||||
LOCATE COMP "sdram_a[7]" SITE "J20"; | |||||
LOCATE COMP "sdram_a[8]" SITE "J19"; | |||||
LOCATE COMP "sdram_a[9]" SITE "H20"; | |||||
LOCATE COMP "sdram_a[10]" SITE "N19"; | |||||
LOCATE COMP "sdram_a[11]" SITE "G20"; | |||||
LOCATE COMP "sdram_a[12]" SITE "G19"; | |||||
LOCATE COMP "sdram_ba[0]" SITE "P19"; | |||||
LOCATE COMP "sdram_ba[1]" SITE "N20"; | |||||
LOCATE COMP "sdram_dqm[0]" SITE "U19"; | |||||
LOCATE COMP "sdram_dqm[1]" SITE "E20"; | |||||
LOCATE COMP "sdram_d[0]" SITE "J16"; | |||||
LOCATE COMP "sdram_d[1]" SITE "L18"; | |||||
LOCATE COMP "sdram_d[2]" SITE "M18"; | |||||
LOCATE COMP "sdram_d[3]" SITE "N18"; | |||||
LOCATE COMP "sdram_d[4]" SITE "P18"; | |||||
LOCATE COMP "sdram_d[5]" SITE "T18"; | |||||
LOCATE COMP "sdram_d[6]" SITE "T17"; | |||||
LOCATE COMP "sdram_d[7]" SITE "U20"; | |||||
LOCATE COMP "sdram_d[8]" SITE "E19"; | |||||
LOCATE COMP "sdram_d[9]" SITE "D20"; | |||||
LOCATE COMP "sdram_d[10]" SITE "D19"; | |||||
LOCATE COMP "sdram_d[11]" SITE "C20"; | |||||
LOCATE COMP "sdram_d[12]" SITE "E18"; | |||||
LOCATE COMP "sdram_d[13]" SITE "F18"; | |||||
LOCATE COMP "sdram_d[14]" SITE "J18"; | |||||
LOCATE COMP "sdram_d[15]" SITE "J17"; | |||||
IOBUF PORT "sdram_clk" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_cke" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_csn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_wen" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_rasn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_casn" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_a[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_a[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_a[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_a[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_a[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_a[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_a[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_a[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_a[8]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_a[9]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_a[10]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_a[11]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_a[12]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_ba[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_ba[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_dqm[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_dqm[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_d[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_d[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_d[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_d[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_d[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_d[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_d[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_d[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_d[8]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_d[9]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_d[10]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_d[11]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_d[12]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_d[13]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_d[14]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "sdram_d[15]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4; | |||||
# GPDI differential interface (Video) "gpdi" sheet | |||||
LOCATE COMP "gpdi_dp[0]" SITE "A16"; # Blue + | |||||
LOCATE COMP "gpdi_dn[0]" SITE "B16"; # Blue - | |||||
LOCATE COMP "gpdi_dp[1]" SITE "A14"; # Green + | |||||
LOCATE COMP "gpdi_dn[1]" SITE "C14"; # Green - | |||||
LOCATE COMP "gpdi_dp[2]" SITE "A12"; # Red + | |||||
LOCATE COMP "gpdi_dn[2]" SITE "A13"; # Red - | |||||
LOCATE COMP "gpdi_dp[3]" SITE "A17"; # Clock + | |||||
LOCATE COMP "gpdi_dn[3]" SITE "B18"; # Clock - | |||||
LOCATE COMP "gpdi_ethp" SITE "A19"; # Ethernet + | |||||
LOCATE COMP "gpdi_ethn" SITE "B20"; # Ethernet - | |||||
LOCATE COMP "gpdi_cec" SITE "A18"; | |||||
LOCATE COMP "gpdi_sda" SITE "B19"; # I2C shared with RTC | |||||
LOCATE COMP "gpdi_scl" SITE "E12"; # I2C shared with RTC C12->E12 | |||||
IOBUF PORT "gpdi_dp[0]" IO_TYPE=LVCMOS33D DRIVE=4; | |||||
IOBUF PORT "gpdi_dn[0]" IO_TYPE=LVCMOS33D DRIVE=4; | |||||
IOBUF PORT "gpdi_dp[1]" IO_TYPE=LVCMOS33D DRIVE=4; | |||||
IOBUF PORT "gpdi_dn[1]" IO_TYPE=LVCMOS33D DRIVE=4; | |||||
IOBUF PORT "gpdi_dp[2]" IO_TYPE=LVCMOS33D DRIVE=4; | |||||
IOBUF PORT "gpdi_dn[2]" IO_TYPE=LVCMOS33D DRIVE=4; | |||||
IOBUF PORT "gpdi_dp[3]" IO_TYPE=LVCMOS33D DRIVE=4; | |||||
IOBUF PORT "gpdi_dn[3]" IO_TYPE=LVCMOS33D DRIVE=4; | |||||
IOBUF PORT "gpdi_ethp" IO_TYPE=LVCMOS33D DRIVE=4; | |||||
IOBUF PORT "gpdi_ethn" IO_TYPE=LVCMOS33D DRIVE=4; | |||||
IOBUF PORT "gpdi_cec" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gpdi_sda" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gpdi_scl" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
# GPIO (default single-ended) "gpio", "ram", "gpdi" sheet | |||||
# Pins enumerated gp[0-27], gn[0-27]. | |||||
# With differential mode enabled on Lattice, | |||||
# gp[] (+) are used, gn[] (-) are ignored from design | |||||
# as they handle inverted signal by default. | |||||
# To enable differential, rename LVCMOS33->LVCMOS33D | |||||
LOCATE COMP "gp[0]" SITE "B11"; # J1_5+ GP0 | |||||
LOCATE COMP "gn[0]" SITE "C11"; # J1_5- GN0 | |||||
LOCATE COMP "gp[1]" SITE "A10"; # J1_7+ GP1 | |||||
LOCATE COMP "gn[1]" SITE "A11"; # J1_7- GN1 | |||||
LOCATE COMP "gp[2]" SITE "A9"; # J1_9+ GP2 | |||||
LOCATE COMP "gn[2]" SITE "B10"; # J1_9- GN2 | |||||
LOCATE COMP "gp[3]" SITE "B9"; # J1_11+ GP3 | |||||
LOCATE COMP "gn[3]" SITE "C10"; # J1_11- GN3 | |||||
LOCATE COMP "gp[4]" SITE "A7"; # J1_13+ GP4 | |||||
LOCATE COMP "gn[4]" SITE "A8"; # J1_13- GN4 | |||||
LOCATE COMP "gp[5]" SITE "C8"; # J1_15+ GP5 | |||||
LOCATE COMP "gn[5]" SITE "B8"; # J1_15- GN5 | |||||
LOCATE COMP "gp[6]" SITE "C6"; # J1_17+ GP6 | |||||
LOCATE COMP "gn[6]" SITE "C7"; # J1_17- GN6 | |||||
IOBUF PORT "gp[0]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[0]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[1]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[1]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[2]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[2]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[3]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[3]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[4]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[4]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[5]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[5]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[6]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[6]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
LOCATE COMP "gp[7]" SITE "A6"; # J1_23+ GP7 | |||||
LOCATE COMP "gn[7]" SITE "B6"; # J1_23- GN7 | |||||
LOCATE COMP "gp[8]" SITE "A4"; # J1_25+ GP8 | |||||
LOCATE COMP "gn[8]" SITE "A5"; # J1_25- GN8 | |||||
LOCATE COMP "gp[9]" SITE "A2"; # J1_27+ GP9 | |||||
LOCATE COMP "gn[9]" SITE "B1"; # J1_27- GN9 | |||||
LOCATE COMP "gp[10]" SITE "C4"; # J1_29+ GP10 WIFI_GPIO27 | |||||
LOCATE COMP "gn[10]" SITE "B4"; # J1_29- GN10 | |||||
LOCATE COMP "gp[11]" SITE "F4"; # J1_31+ GP11 WIFI_GPIO25 | |||||
LOCATE COMP "gn[11]" SITE "E3"; # J1_31- GN11 WIFI_GPIO26 | |||||
LOCATE COMP "gp[12]" SITE "G3"; # J1_33+ GP12 WIFI_GPIO32 | |||||
LOCATE COMP "gn[12]" SITE "F3"; # J1_33- GN12 WIFI_GPIO33 | |||||
LOCATE COMP "gp[13]" SITE "H4"; # J1_35+ GP13 WIFI_GPIO34 | |||||
LOCATE COMP "gn[13]" SITE "G5"; # J1_35- GN13 WIFI_GPIO35 | |||||
IOBUF PORT "gp[7]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[7]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[8]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[8]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[9]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[9]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[10]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[10]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[11]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[11]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[12]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[12]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[13]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[13]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
LOCATE COMP "gp[14]" SITE "U18"; # J2_5+ GP14 | |||||
LOCATE COMP "gn[14]" SITE "U17"; # J2_5- GN14 | |||||
LOCATE COMP "gp[15]" SITE "N17"; # J2_7+ GP15 | |||||
LOCATE COMP "gn[15]" SITE "P16"; # J2_7- GN15 | |||||
LOCATE COMP "gp[16]" SITE "N16"; # J2_9+ GP16 | |||||
LOCATE COMP "gn[16]" SITE "M17"; # J2_9- GN16 | |||||
LOCATE COMP "gp[17]" SITE "L16"; # J2_11+ GP17 | |||||
LOCATE COMP "gn[17]" SITE "L17"; # J2_11- GN17 | |||||
LOCATE COMP "gp[18]" SITE "H18"; # J2_13+ GP18 | |||||
LOCATE COMP "gn[18]" SITE "H17"; # J2_13- GN18 | |||||
LOCATE COMP "gp[19]" SITE "F17"; # J2_15+ GP19 | |||||
LOCATE COMP "gn[19]" SITE "G18"; # J2_15- GN19 | |||||
LOCATE COMP "gp[20]" SITE "D18"; # J2_17+ GP20 | |||||
LOCATE COMP "gn[20]" SITE "E17"; # J2_17- GN20 | |||||
IOBUF PORT "gp[14]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[14]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[15]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[15]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[16]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[16]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[17]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[17]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[18]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[18]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[19]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[19]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[20]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[20]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
LOCATE COMP "io_jtag_tms" SITE "C18"; # J2_23+ GP21 | |||||
LOCATE COMP "gn[21]" SITE "D17"; # J2_23- GN21 | |||||
LOCATE COMP "io_jtag_tdi" SITE "B15"; # J2_25+ GP22 D15->B15 | |||||
LOCATE COMP "gn[22]" SITE "C15"; # J2_25- GN22 E15->C15 | |||||
LOCATE COMP "io_jtag_tdo" SITE "B17"; # J2_27+ GP23 | |||||
LOCATE COMP "gn[23]" SITE "C17"; # J2_27- GN23 | |||||
LOCATE COMP "io_jtag_tck" SITE "C16"; # J2_29+ GP24 | |||||
LOCATE COMP "gn[24]" SITE "D16"; # J2_29- GN24 | |||||
LOCATE COMP "gp[25]" SITE "D14"; # J2_31+ GP25 B15->D14 | |||||
LOCATE COMP "gn[25]" SITE "E14"; # J2_31- GN25 C15->E14 | |||||
LOCATE COMP "gp[26]" SITE "B13"; # J2_33+ GP26 | |||||
LOCATE COMP "gn[26]" SITE "C13"; # J2_33- GN26 | |||||
LOCATE COMP "gp[27]" SITE "D13"; # J2_35+ GP27 | |||||
LOCATE COMP "gn[27]" SITE "E13"; # J2_35- GN27 | |||||
IOBUF PORT "io_jtag_tms" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[21]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "io_jtag_tdi" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[22]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "io_jtag_tdo" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[23]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "io_jtag_tck" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[24]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[25]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[25]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[26]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[26]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gp[27]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
IOBUF PORT "gn[27]" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
## PROGRAMN (reload bitstream from FLASH, exit from bootloader) | |||||
# PCB v2.0.5 and higher | |||||
LOCATE COMP "user_programn" SITE "M4"; | |||||
IOBUF PORT "user_programn" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4; | |||||
## SHUTDOWN "power", "ram" sheet (connected from PCB v1.7.5) | |||||
# on PCB v1.7 shutdown is not connected to FPGA | |||||
LOCATE COMP "shutdown" SITE "G16"; # FPGA receives | |||||
IOBUF PORT "shutdown" PULLMODE=DOWN IO_TYPE=LVCMOS33 DRIVE=4; |
@ -0,0 +1,35 @@ | |||||
# ---> SBT | |||||
# Simple Build Tool | |||||
# http://www.scala-sbt.org/release/docs/Getting-Started/Directories.html#configuring-version-control | |||||
dist/* | |||||
target/ | |||||
lib_managed/ | |||||
src_managed/ | |||||
project/boot/ | |||||
project/plugins/project/ | |||||
.history | |||||
.cache | |||||
.lib/ | |||||
.ensime | |||||
.idea | |||||
# ---> Scala | |||||
*.class | |||||
*.log | |||||
.metals | |||||
# ---> Spinal | |||||
simWorkspace/ | |||||
tmp/ | |||||
# ---> Project | |||||
rtl/.Xil | |||||
rtl/*.log | |||||
rtl/*.dcp | |||||
rtl/*.time | |||||
rtl/*.json | |||||
rtl/*.bit | |||||
rtl/*.v | |||||
cpu0.yaml | |||||
*.bin |
@ -0,0 +1,20 @@ | |||||
ThisBuild / organization := "mupq" | |||||
ThisBuild / scalaVersion := "2.11.12" | |||||
lazy val pqvexriscv = (project in file(".")) | |||||
.settings( | |||||
name := "pqvexriscv", | |||||
version := "0.1", | |||||
libraryDependencies ++= Seq( | |||||
"org.scalatest" %% "scalatest" % "3.0.5" % "test", | |||||
compilerPlugin("com.github.spinalhdl" % "spinalhdl-idsl-plugin_2.11" % "1.4.0"), | |||||
"com.github.spinalhdl" % "spinalhdl-crypto_2.11" % "latest.release" | |||||
), | |||||
run / connectInput := true, | |||||
outputStrategy := Some(StdoutOutput), | |||||
).dependsOn(vexRiscv) | |||||
lazy val vexRiscv = RootProject(uri("git://github.com/SpinalHDL/VexRiscv#2942d0652a89646c5225bee15dd55cc3b0871766")) | |||||
fork := true |
@ -0,0 +1 @@ | |||||
sbt.version=1.3.13 |
@ -0,0 +1,119 @@ | |||||
package quantumrisc | |||||
import vexriscv._ | |||||
import vexriscv.plugin._ | |||||
import spinal.core._ | |||||
/** | |||||
* A multiplication plugin using only 16-bit multiplications | |||||
*/ | |||||
class Mul16Plugin extends Plugin[VexRiscv]{ | |||||
object MUL_LL extends Stageable(UInt(32 bits)) | |||||
object MUL_LH extends Stageable(UInt(32 bits)) | |||||
object MUL_HL extends Stageable(UInt(32 bits)) | |||||
object MUL_HH extends Stageable(UInt(32 bits)) | |||||
object MUL extends Stageable(Bits(64 bits)) | |||||
object IS_MUL extends Stageable(Bool) | |||||
override def setup(pipeline: VexRiscv): Unit = { | |||||
import Riscv._ | |||||
import pipeline.config._ | |||||
val actions = List[(Stageable[_ <: BaseType],Any)]( | |||||
SRC1_CTRL -> Src1CtrlEnum.RS, | |||||
SRC2_CTRL -> Src2CtrlEnum.RS, | |||||
REGFILE_WRITE_VALID -> True, | |||||
BYPASSABLE_EXECUTE_STAGE -> False, | |||||
BYPASSABLE_MEMORY_STAGE -> False, | |||||
RS1_USE -> True, | |||||
RS2_USE -> True, | |||||
IS_MUL -> True | |||||
) | |||||
val decoderService = pipeline.service(classOf[DecoderService]) | |||||
decoderService.addDefault(IS_MUL, False) | |||||
decoderService.add(List( | |||||
MULX -> actions | |||||
)) | |||||
} | |||||
override def build(pipeline: VexRiscv): Unit = { | |||||
import pipeline._ | |||||
import pipeline.config._ | |||||
// Prepare signed inputs for the multiplier in the next stage. | |||||
// This will map them best to an FPGA DSP. | |||||
execute plug new Area { | |||||
import execute._ | |||||
val a,b = Bits(32 bit) | |||||
a := input(SRC1) | |||||
b := input(SRC2) | |||||
val aLow = a(15 downto 0).asUInt | |||||
val bLow = b(15 downto 0).asUInt | |||||
val aHigh = a(31 downto 16).asUInt | |||||
val bHigh = b(31 downto 16).asUInt | |||||
insert(MUL_LL) := aLow * bLow | |||||
insert(MUL_LH) := aLow * bHigh | |||||
insert(MUL_HL) := aHigh * bLow | |||||
insert(MUL_HH) := aHigh * bHigh | |||||
} | |||||
memory plug new Area { | |||||
import memory._ | |||||
val ll = UInt(32 bits) | |||||
val lh = UInt(33 bits) | |||||
val hl = UInt(32 bits) | |||||
val hh = UInt(32 bits) | |||||
ll := input(MUL_LL) | |||||
lh := input(MUL_LH).resized | |||||
hl := input(MUL_HL) | |||||
hh := input(MUL_HH) | |||||
val hllh = lh + hl | |||||
insert(MUL) := ((hh ## ll(31 downto 16)).asUInt + hllh) ## ll(15 downto 0) | |||||
} | |||||
writeBack plug new Area { | |||||
import writeBack._ | |||||
val aSigned,bSigned = Bool | |||||
switch(input(INSTRUCTION)(13 downto 12)) { | |||||
is(B"01") { | |||||
aSigned := True | |||||
bSigned := True | |||||
} | |||||
is(B"10") { | |||||
aSigned := True | |||||
bSigned := False | |||||
} | |||||
default { | |||||
aSigned := False | |||||
bSigned := False | |||||
} | |||||
} | |||||
val a = (aSigned && input(SRC1).msb) ? input(SRC2).asUInt | U(0) | |||||
val b = (bSigned && input(SRC2).msb) ? input(SRC1).asUInt | U(0) | |||||
when(arbitration.isValid && input(IS_MUL)){ | |||||
switch(input(INSTRUCTION)(13 downto 12)){ | |||||
is(B"00"){ | |||||
output(REGFILE_WRITE_DATA) := input(MUL)(31 downto 0) | |||||
} | |||||
is(B"01",B"10",B"11"){ | |||||
output(REGFILE_WRITE_DATA) := (((input(MUL)(63 downto 32)).asUInt + ~a) + (~b + 2)).asBits | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,47 @@ | |||||
package quantumrisc | |||||
import spinal.core._ | |||||
import spinal.lib._ | |||||
import spinal.lib.bus.amba3.apb._ | |||||
import spinal.crypto.symmetric.aes.AESCore_Std | |||||
object MyMem{ | |||||
def getApb3Config = Apb3Config( | |||||
addressWidth = 20, | |||||
dataWidth = 32 | |||||
) | |||||
} | |||||
class MyMem() extends Component { | |||||
val io = new Bundle { | |||||
val bus = slave(Apb3(MyMem.getApb3Config)) | |||||
} | |||||
// Define a bundle "mapMem" for the state and the status | |||||
// case object mapMem extends Bundle { | |||||
// val state = Reg(Bits(128 bit)) | |||||
// val key = Reg(Bits(128 bit)) | |||||
// val status = Reg(Bits(32 bit)) | |||||
// } | |||||
val aesCore = new AESCore_Std(128 bits) | |||||
// Create a BusslaveFactory on the io.bus | |||||
val busCtrl = Apb3SlaveFactory(io.bus) | |||||
aesCore.io.driveFrom(busCtrl,baseAddress=0x30000) | |||||
// Register the bundle "myReg" to the BusSlaveFactory at address 0x30000 | |||||
// busCtrl.readMultiWord(aesCore.io.rsp,address = 0x30000) | |||||
// busCtrl.writeMultiWord(aesCore.io.cmd,address = 0x30000) | |||||
// When state and key are loaded, the status becomes 1 | |||||
// and the calculation can start (state = state XOR key). | |||||
// After the calcualtion is finished, the status is set to 0. | |||||
// when (mapMem.status === 1){ | |||||
// mapMem.state := mapMem.state ^ mapMem.key | |||||
// mapMem.status := 0 | |||||
// } | |||||
} |
@ -0,0 +1,278 @@ | |||||
package quantumrisc | |||||
import scala.collection.mutable.ArrayBuffer | |||||
import spinal.core._ | |||||
import spinal.lib._ | |||||
import spinal.lib.bus.amba3.apb._ | |||||
import spinal.lib.bus.misc._ | |||||
import spinal.lib.bus.simple._ | |||||
import spinal.lib.io._ | |||||
import spinal.lib.com.jtag._ | |||||
import spinal.lib.com.uart._ | |||||
import vexriscv._ | |||||
import vexriscv.demo.MuraxApb3Timer | |||||
import vexriscv.plugin._ | |||||
abstract class PQVexRiscv( | |||||
cpuPlugins : () => Seq[Plugin[VexRiscv]], | |||||
ibusRange : SizeMapping, | |||||
genUART : Boolean = true, | |||||
gpioWidth : Int = 0, | |||||
genTimer : Boolean = false | |||||
) extends Component { | |||||
val coreFrequency : HertzNumber | |||||
/* Clock and resets */ | |||||
val asyncReset: Bool = Bool | |||||
val mainClock: Bool = Bool | |||||
val resetCtrlClockDomain: ClockDomain = ClockDomain( | |||||
clock = mainClock, | |||||
config = ClockDomainConfig(resetKind = BOOT)) | |||||
val resetCtrl = new ClockingArea(resetCtrlClockDomain) { | |||||
val bufferedReset = BufferCC(asyncReset) | |||||
val mainClockReset = RegNext(bufferedReset) | |||||
val systemClockReset = RegNext(bufferedReset) | |||||
} | |||||
val systemClockDomain: ClockDomain = ClockDomain( | |||||
clock = mainClock, | |||||
reset = resetCtrl.systemClockReset, | |||||
frequency = FixedFrequency(coreFrequency)) | |||||
val debugClockDomain: ClockDomain = ClockDomain( | |||||
clock = mainClock, | |||||
reset = resetCtrl.mainClockReset, | |||||
frequency = FixedFrequency(coreFrequency)) | |||||
/* Bus interconnect */ | |||||
val busConfig = PipelinedMemoryBusConfig( | |||||
addressWidth = 32, | |||||
dataWidth = 32 | |||||
) | |||||
val busSlaves = ArrayBuffer[(PipelinedMemoryBus, SizeMapping)]() | |||||
val busMasters = ArrayBuffer[(PipelinedMemoryBus, SizeMapping)]() | |||||
/* VexRiscv Core */ | |||||
var jtag : Jtag = null | |||||
val core = new ClockingArea(systemClockDomain) { | |||||
val timerInterrupt = False | |||||
val externalInterrupt = False | |||||
val config = VexRiscvConfig( | |||||
plugins = cpuPlugins() ++ Seq(new DebugPlugin(debugClockDomain, 3))) | |||||
val cpu = new VexRiscv(config) | |||||
/* Wire the Busses / Lines to the plugins */ | |||||
var ibus : PipelinedMemoryBus = PipelinedMemoryBus(busConfig) | |||||
var dbus : PipelinedMemoryBus = PipelinedMemoryBus(busConfig) | |||||
for (plugin <- cpu.plugins) plugin match { | |||||
case plugin: IBusSimplePlugin => | |||||
val cpuibus = plugin.iBus.toPipelinedMemoryBus() | |||||
ibus.cmd <-/< cpuibus.cmd | |||||
ibus.rsp >> cpuibus.rsp | |||||
case plugin: DBusSimplePlugin => | |||||
val cpudbus = plugin.dBus.toPipelinedMemoryBus() | |||||
dbus.cmd <-/< cpudbus.cmd | |||||
dbus.rsp >> cpudbus.rsp | |||||
plugin.dBus.rsp.error := False | |||||
case plugin: CsrPlugin => | |||||
plugin.externalInterrupt := externalInterrupt | |||||
plugin.timerInterrupt := timerInterrupt | |||||
case plugin: DebugPlugin => | |||||
plugin.debugClockDomain { | |||||
resetCtrl.systemClockReset setWhen (RegNext(plugin.io.resetOut)) | |||||
jtag = plugin.io.bus.fromJtag() | |||||
} | |||||
case _ => | |||||
} | |||||
busMasters += dbus -> SizeMapping(0l, (1l << 32l)) | |||||
busMasters += ibus -> ibusRange | |||||
} | |||||
/* Peripherals */ | |||||
var gpio : TriStateArray = null | |||||
var uart : Uart = null | |||||
val peripherals = new ClockingArea(systemClockDomain) { | |||||
if (gpioWidth > 0) { | |||||
gpio = TriStateArray(gpioWidth bits) | |||||
} | |||||
if (genUART) { | |||||
uart = Uart() | |||||
} | |||||
if(genUART || gpioWidth > 0 || genTimer) { | |||||
val apbBridge = new PipelinedMemoryBusToApbBridge( | |||||
apb3Config = Apb3Config( | |||||
addressWidth = 20, | |||||
dataWidth = 32 | |||||
), | |||||
pipelineBridge = false, | |||||
pipelinedMemoryBusConfig = busConfig | |||||
) | |||||
busSlaves += apbBridge.io.pipelinedMemoryBus -> SizeMapping(0xF0000000l, 1 MiB) | |||||
val apbMapping = ArrayBuffer[(Apb3, SizeMapping)]() | |||||
if (gpioWidth > 0) { | |||||
val gpioACtrl = Apb3Gpio(gpioWidth = gpioWidth, withReadSync = true) | |||||
gpio <> gpioACtrl.io.gpio | |||||
apbMapping += gpioACtrl.io.apb -> (0x00000, 4 KiB) | |||||
} | |||||
if (genUART) { | |||||
val uartCtrlConfig = UartCtrlMemoryMappedConfig( | |||||
uartCtrlConfig = UartCtrlGenerics( | |||||
dataWidthMax = 8, | |||||
clockDividerWidth = 20, | |||||
preSamplingSize = 1, | |||||
samplingSize = 3, | |||||
postSamplingSize = 1 | |||||
), | |||||
initConfig = UartCtrlInitConfig( | |||||
baudrate = 115200, | |||||
dataLength = 7, //7 => 8 bits | |||||
parity = UartParityType.NONE, | |||||
stop = UartStopType.ONE | |||||
), | |||||
busCanWriteClockDividerConfig = false, | |||||
busCanWriteFrameConfig = false, | |||||
txFifoDepth = 16, | |||||
rxFifoDepth = 16 | |||||
) | |||||
val uartCtrl = Apb3UartCtrl(uartCtrlConfig) | |||||
uart <> uartCtrl.io.uart | |||||
core.externalInterrupt setWhen(uartCtrl.io.interrupt) | |||||
apbMapping += uartCtrl.io.apb -> (0x10000, 4 KiB) | |||||
} | |||||
if (genTimer) { | |||||
val timer = new MuraxApb3Timer() | |||||
core.timerInterrupt setWhen(timer.io.interrupt) | |||||
apbMapping += timer.io.apb -> (0x20000, 4 KiB) | |||||
} | |||||
val myMem = new MyMem() | |||||
// core.timerInterrupt setWhen(myMem.io.interrupt) | |||||
apbMapping += myMem.io.bus -> (0x30000, 4 KiB) | |||||
val apbDecoder = Apb3Decoder( | |||||
master = apbBridge.io.apb, | |||||
slaves = apbMapping | |||||
) | |||||
} | |||||
} | |||||
def buildInterconnect() : Unit = { | |||||
assert(!SizeMapping.verifyOverlapping(busSlaves.map(_._2))) | |||||
val crossbar = new ClockingArea(systemClockDomain) { | |||||
val interconnect = new PipelinedMemoryBusInterconnect() | |||||
interconnect.perfConfig() | |||||
/* Setup the interconnect */ | |||||
interconnect.addSlaves(busSlaves: _*) | |||||
/* Check which masters overlap with which slaves */ | |||||
def overlaps(a : SizeMapping, b : SizeMapping) : Boolean = if (a.base < b.base) a.end >= b.base else b.end >= a.base | |||||
interconnect.addMasters(busMasters.map(m => m._1 -> busSlaves.filter(s => overlaps(m._2, s._2)).map(s => s._1).toSeq): _*) | |||||
} | |||||
} | |||||
Component.current.addPrePopTask(() => buildInterconnect()) | |||||
} | |||||
object PQVexRiscv | |||||
{ | |||||
type PluginSeq = Seq[Plugin[VexRiscv]] | |||||
type PluginGen = () => PluginSeq | |||||
/** Basic set of Plugins (conforms mostly to rv32i) */ | |||||
def baseConfig(base: PluginGen = () => Seq()) = () => base() ++ Seq( | |||||
new IBusSimplePlugin( | |||||
resetVector = 0x80000000l, | |||||
cmdForkOnSecondStage = true, | |||||
cmdForkPersistence = false, | |||||
prediction = NONE, | |||||
catchAccessFault = false, | |||||
compressedGen = false | |||||
), | |||||
new DBusSimplePlugin( | |||||
catchAddressMisaligned = false, | |||||
catchAccessFault = false, | |||||
earlyInjection = false | |||||
), | |||||
new CsrPlugin( | |||||
CsrPluginConfig.smallest(0x80000000l).copy( | |||||
mtvecAccess = CsrAccess.READ_WRITE, | |||||
mcycleAccess = CsrAccess.READ_ONLY, | |||||
minstretAccess = CsrAccess.READ_ONLY | |||||
) | |||||
), | |||||
new DecoderSimplePlugin( | |||||
catchIllegalInstruction = false | |||||
), | |||||
new RegFilePlugin( | |||||
regFileReadyKind = plugin.SYNC, | |||||
zeroBoot = false | |||||
), | |||||
new IntAluPlugin, | |||||
new SrcPlugin( | |||||
separatedAddSub = false, | |||||
executeInsertion = false | |||||
), | |||||
new FullBarrelShifterPlugin, | |||||
new HazardSimplePlugin( | |||||
bypassExecute = true, | |||||
bypassMemory = true, | |||||
bypassWriteBack = true, | |||||
bypassWriteBackBuffer = true, | |||||
pessimisticUseSrc = false, | |||||
pessimisticWriteRegFile = false, | |||||
pessimisticAddressMatch = false | |||||
), | |||||
new BranchPlugin( | |||||
earlyBranch = false, | |||||
catchAddressMisaligned = false | |||||
), | |||||
new YamlPlugin("cpu0.yaml") | |||||
) | |||||
/** Plugins for a small multiplier */ | |||||
def smallMultiplier = Seq( | |||||
new MulDivIterativePlugin( | |||||
genMul = true, | |||||
genDiv = true, | |||||
mulUnrollFactor = 1, | |||||
divUnrollFactor = 1 | |||||
) | |||||
) | |||||
/** Config with a small multiplier */ | |||||
def withSmallMultiplier(base: PluginGen = baseConfig()) = () => base() ++ smallMultiplier | |||||
/** Plugins for a multiplier for FPGAs */ | |||||
def dspMultiplier = Seq( | |||||
new Mul16Plugin, | |||||
new MulDivIterativePlugin( | |||||
genMul = false, | |||||
genDiv = true, | |||||
divUnrollFactor = 1 | |||||
) | |||||
) | |||||
/** Config with a multiplier for FPGAs */ | |||||
def withDSPMultiplier(base: PluginGen = baseConfig()) = () => base() ++ dspMultiplier | |||||
} |
@ -0,0 +1,174 @@ | |||||
package quantumrisc | |||||
import java.io.{File, FileInputStream, FileOutputStream, IOException, OutputStream} | |||||
import scopt.OptionParser | |||||
import spinal.sim._ | |||||
import spinal.core._ | |||||
import spinal.lib._ | |||||
import spinal.core.sim._ | |||||
import spinal.lib.bus.simple._ | |||||
import spinal.lib.bus.misc.SizeMapping | |||||
import spinal.lib.io.TriStateArray | |||||
import spinal.lib.com.jtag.Jtag | |||||
import spinal.lib.com.uart.Uart | |||||
import spinal.lib.com.jtag.sim.JtagTcp | |||||
import vexriscv.VexRiscv | |||||
import vexriscv.plugin.Plugin | |||||
case class PipelinedMemoryBusRam(size : BigInt, initialContent : File = null) extends Component{ | |||||
require(size % 4 == 0, "Size must be multiple of 4 bytes") | |||||
require(size > 0, "Size must be greater than zero") | |||||
val busConfig = PipelinedMemoryBusConfig(log2Up(size), 32) | |||||
val io = new Bundle{ | |||||
val bus = slave(PipelinedMemoryBus(busConfig)) | |||||
} | |||||
val ram = Mem(Bits(32 bits), size / 4) | |||||
io.bus.rsp.valid := RegNext(io.bus.cmd.fire && !io.bus.cmd.write) init(False) | |||||
io.bus.rsp.data := ram.readWriteSync( | |||||
address = io.bus.cmd.address >> 2, | |||||
data = io.bus.cmd.data, | |||||
enable = io.bus.cmd.valid, | |||||
write = io.bus.cmd.write, | |||||
mask = io.bus.cmd.mask | |||||
) | |||||
io.bus.cmd.ready := True | |||||
if (initialContent != null) { | |||||
val input = new FileInputStream(initialContent) | |||||
val initContent = Array.fill[BigInt](ram.wordCount)(0) | |||||
val fileContent = Array.ofDim[Byte](Seq(input.available, initContent.length * 4).min) | |||||
input.read(fileContent) | |||||
for ((byte, addr) <- fileContent.zipWithIndex) { | |||||
val l = java.lang.Byte.toUnsignedLong(byte) << ((addr & 3) * 8) | |||||
initContent(addr >> 2) |= BigInt(l) | |||||
} | |||||
ram.initBigInt(initContent) | |||||
} | |||||
} | |||||
class PQVexRiscvSim( | |||||
val ramBlockSizes : Seq[BigInt] = Seq[BigInt](256 KiB, 128 KiB), | |||||
val initialContent : File = null, | |||||
val coreFrequency : HertzNumber = 12 MHz, | |||||
cpuPlugins : () => Seq[Plugin[VexRiscv]] = PQVexRiscv.withDSPMultiplier() | |||||
) extends PQVexRiscv( | |||||
cpuPlugins = cpuPlugins, | |||||
ibusRange = SizeMapping(0x80000000l, ramBlockSizes.reduce(_ + _)) | |||||
) { | |||||
val io = new Bundle { | |||||
val asyncReset = in Bool | |||||
val mainClock = in Bool | |||||
val uart = master(Uart()) | |||||
val jtag = slave(Jtag()) | |||||
} | |||||
asyncReset := io.asyncReset | |||||
mainClock := io.mainClock | |||||
uart <> io.uart | |||||
jtag <> io.jtag | |||||
val memory = new ClockingArea(systemClockDomain) { | |||||
val ramBlocks = ramBlockSizes.zipWithIndex.map(t => PipelinedMemoryBusRam(t._1, if (t._2 == 0) initialContent else null)) | |||||
var curAddr : BigInt = 0x80000000l | |||||
for (block <- ramBlocks) { | |||||
busSlaves += block.io.bus -> SizeMapping(curAddr, block.size) | |||||
curAddr += block.size | |||||
} | |||||
} | |||||
} | |||||
object PQVexRiscvSim { | |||||
def main(args: Array[String]) = { | |||||
case class PQVexRiscvSimConfig( | |||||
uartOutFile: OutputStream = System.out, | |||||
initFile: File = null, | |||||
ramBlocks: Seq[BigInt] = Seq(256 KiB, 128 KiB), | |||||
cpuPlugins: () => Seq[Plugin[VexRiscv]] = PQVexRiscv.withDSPMultiplier() | |||||
) | |||||
val optParser = new OptionParser[PQVexRiscvSimConfig]("PQVexRiscvSim") { | |||||
head("PQVexRiscvSim simulator") | |||||
help("help") text("print usage text") | |||||
opt[File]("uart") action((f, c) => c.copy(uartOutFile = new FileOutputStream(f, true))) text("File for UART output (will be appended)") valueName("<output>") | |||||
opt[File]("init") action((f, c) => c.copy(initFile = f)) text("Initialization file for first RAM block") valueName("<bin>") | |||||
opt[Seq[Int]]("ram") action((r, c) => c.copy(ramBlocks = r.map(_ KiB))) text("SRAM Blocks in KiB") valueName("<block1>,<block2>") | |||||
} | |||||
val config = optParser.parse(args, PQVexRiscvSimConfig()) match { | |||||
case Some(config) => config | |||||
case None => ??? | |||||
} | |||||
val compiled = SimConfig.allOptimisation.compile { | |||||
new PQVexRiscvSim(config.ramBlocks, config.initFile, cpuPlugins=config.cpuPlugins) | |||||
} | |||||
compiled.doSim("PqVexRiscvSim", 42) { dut => | |||||
val mainClkPeriod = (1e12 / dut.coreFrequency.toDouble).toLong | |||||
val jtagClkPeriod = mainClkPeriod * 4 | |||||
val uartBaudRate = 115200 | |||||
val uartBaudPeriod = (1e12 / uartBaudRate.toDouble).toLong | |||||
val clockDomain = ClockDomain(dut.io.mainClock, dut.io.asyncReset) | |||||
clockDomain.forkStimulus(mainClkPeriod) | |||||
val tcpJtag = JtagTcp( | |||||
jtag = dut.io.jtag, | |||||
jtagClkPeriod = jtagClkPeriod | |||||
) | |||||
println(s"Simulating ${dut.getClass.getName} with JtagTcp on port 7894") | |||||
val uartPin = dut.io.uart.txd | |||||
val uartDecoder = fork { | |||||
sleep(1) | |||||
waitUntil(uartPin.toBoolean == true) | |||||
try { | |||||
while (true) { | |||||
waitUntil(uartPin.toBoolean == false) | |||||
sleep(uartBaudPeriod / 2) | |||||
if (uartPin.toBoolean != false) { | |||||
println("\rUART frame error (start bit)") | |||||
} else { | |||||
sleep(uartBaudPeriod) | |||||
var byte = 0 | |||||
var i = 0 | |||||
while (i < 8) { | |||||
if (uartPin.toBoolean) { | |||||
byte |= 1 << i | |||||
} | |||||
sleep(uartBaudPeriod) | |||||
i += 1 | |||||
} | |||||
if (uartPin.toBoolean) { | |||||
config.uartOutFile.write(byte) | |||||
} else { | |||||
println("\rUART frame error (stop bit)") | |||||
} | |||||
} | |||||
} | |||||
} catch { | |||||
case io: IOException => | |||||
} | |||||
println("\rUART decoder stopped") | |||||
} | |||||
var running = true | |||||
while (running) { | |||||
sleep(mainClkPeriod * 50000) | |||||
} | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,111 @@ | |||||
package quantumrisc | |||||
import java.io.{File, FileInputStream, FileOutputStream, IOException, OutputStream} | |||||
import scopt.OptionParser | |||||
import spinal.sim._ | |||||
import spinal.core._ | |||||
import spinal.lib._ | |||||
import spinal.core.sim._ | |||||
import spinal.lib.bus.simple._ | |||||
import spinal.lib.bus.misc.SizeMapping | |||||
import spinal.lib.io.{TriStateArray, InOutWrapper} | |||||
import spinal.lib.com.jtag.Jtag | |||||
import spinal.lib.com.uart.Uart | |||||
import spinal.lib.com.jtag.sim.JtagTcp | |||||
import vexriscv.VexRiscv | |||||
import vexriscv.plugin.Plugin | |||||
case class PipelinedMemoryBusRamUlx3s(size : BigInt, initialContent : File = null) extends Component{ | |||||
require(size % 4 == 0, "Size must be multiple of 4 bytes") | |||||
require(size > 0, "Size must be greater than zero") | |||||
val busConfig = PipelinedMemoryBusConfig(log2Up(size), 32) | |||||
val io = new Bundle{ | |||||
val bus = slave(PipelinedMemoryBus(busConfig)) | |||||
} | |||||
val ram = Mem(Bits(32 bits), size / 4) | |||||
io.bus.rsp.valid := RegNext(io.bus.cmd.fire && !io.bus.cmd.write) init(False) | |||||
io.bus.rsp.data := ram.readWriteSync( | |||||
address = io.bus.cmd.address >> 2, | |||||
data = io.bus.cmd.data, | |||||
enable = io.bus.cmd.valid, | |||||
write = io.bus.cmd.write, | |||||
mask = io.bus.cmd.mask | |||||
) | |||||
io.bus.cmd.ready := True | |||||
if (initialContent != null) { | |||||
val input = new FileInputStream(initialContent) | |||||
val initContent = Array.fill[BigInt](ram.wordCount)(0) | |||||
val fileContent = Array.ofDim[Byte](Seq(input.available, initContent.length * 4).min) | |||||
input.read(fileContent) | |||||
for ((byte, addr) <- fileContent.zipWithIndex) { | |||||
val l = java.lang.Byte.toUnsignedLong(byte) << ((addr & 3) * 8) | |||||
initContent(addr >> 2) |= BigInt(l) | |||||
} | |||||
ram.initBigInt(initContent) | |||||
} | |||||
} | |||||
class PQVexRiscvUlx3s( | |||||
val ramBlockSizes : Seq[BigInt] = Seq[BigInt](256 KiB, 128 KiB), | |||||
val initialContent : File = null, | |||||
val coreFrequency : HertzNumber = 25 MHz, | |||||
cpuPlugins : () => Seq[Plugin[VexRiscv]] = PQVexRiscv.withDSPMultiplier() | |||||
) extends PQVexRiscv( | |||||
cpuPlugins = cpuPlugins, | |||||
ibusRange = SizeMapping(0x80000000l, ramBlockSizes.reduce(_ + _)) | |||||
) { | |||||
val io = new Bundle { | |||||
val asyncReset = in Bool | |||||
val mainClock = in Bool | |||||
val uart = master(Uart()) | |||||
val jtag = slave(Jtag()) | |||||
} | |||||
asyncReset := io.asyncReset | |||||
mainClock := io.mainClock | |||||
uart <> io.uart | |||||
jtag <> io.jtag | |||||
val memory = new ClockingArea(systemClockDomain) { | |||||
val ramBlocks = ramBlockSizes.zipWithIndex.map(t => PipelinedMemoryBusRamUlx3s(t._1, if (t._2 == 0) initialContent else null)) | |||||
var curAddr : BigInt = 0x80000000l | |||||
for (block <- ramBlocks) { | |||||
busSlaves += block.io.bus -> SizeMapping(curAddr, block.size) | |||||
curAddr += block.size | |||||
} | |||||
} | |||||
} | |||||
object PQVexRiscvUlx3s { | |||||
def main(args: Array[String]) : Unit = { | |||||
case class PQVexRiscvUlx3sConfig( | |||||
initFile: File = null | |||||
) | |||||
val optParser = new OptionParser[PQVexRiscvUlx3sConfig]("PQVexRiscvUlx3s") { | |||||
head("PQVexRiscvUlx3s") | |||||
help("help") text("print usage text") | |||||
opt[File]("init") action((f, c) => c.copy(initFile = f)) text("Initialization file for first RAM block") valueName("<bin>") | |||||
} | |||||
val config = optParser.parse(args, PQVexRiscvUlx3sConfig()) match { | |||||
case Some(config) => config | |||||
case None => ??? | |||||
} | |||||
SpinalConfig( | |||||
mode = Verilog | |||||
// targetDirectory = "rtl" | |||||
).generate(new PQVexRiscvUlx3s(initialContent = config.initFile)).printPruned() | |||||
} | |||||
} |
@ -0,0 +1,19 @@ | |||||
# ---- FT232H Adapter ----- | |||||
interface ftdi | |||||
# source [find interface/ftdi/um232h.cfg] | |||||
# ftdi_serial "TN34GV0F" | |||||
ftdi_vid_pid 0x0403 0x6014 | |||||
ftdi_layout_init 0x0008 0x400b | |||||
adapter_khz 20000 | |||||
transport select jtag | |||||
# Adapt this to your favourite FTDI-based debugger | |||||
# source [find interface/ftdi/um232h.cfg] | |||||
# ftdi_serial "TN34GV0F" | |||||
# The Murax target needs a YAML file, even if it is empty | |||||
set MURAX_CPU0_YAML cpu0.yaml | |||||
# The Murax target should work for all PQVexRiscv based chips | |||||
source [find target/murax.cfg] |
@ -0,0 +1,8 @@ | |||||
# Adapt this to your favourite FTDI-based debugger | |||||
source [find interface/jtag_tcp.cfg] | |||||
# The Murax target needs a YAML file, even if it is empty | |||||
set MURAX_CPU0_YAML cpu0.yaml | |||||
# The Murax target should work for all PQVexRiscv based chips | |||||
source [find target/murax.cfg] |