mirror of
https://github.com/tillitis/tillitis-key1.git
synced 2025-05-02 14:16:28 -04:00
Make initial public release
This commit is contained in:
commit
715de60f4a
251 changed files with 881225 additions and 0 deletions
291
hw/application_fpga/Makefile
Normal file
291
hw/application_fpga/Makefile
Normal file
|
@ -0,0 +1,291 @@
|
|||
#=======================================================================
|
||||
#
|
||||
# Makefile
|
||||
# --------
|
||||
# Makefile for building, simulating, running all application_fpga
|
||||
# HW targets as well as its firmware.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 2022 - Tillitis AB
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
#=======================================================================
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# Defines.
|
||||
#-------------------------------------------------------------------
|
||||
SHELL := /bin/bash
|
||||
CUR_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||
P := $(CUR_DIR)
|
||||
|
||||
YOSYS_PATH ?=
|
||||
NEXTPNR_PATH ?=
|
||||
ICESTORM_PATH ?=
|
||||
|
||||
# Size in 32-bit words
|
||||
BRAM_FW_SIZE ?= 2048
|
||||
|
||||
PIN_FILE ?= application_fpga_mta1_usb_v1.pcf
|
||||
|
||||
SIZE ?= llvm-size-14
|
||||
OBJCOPY ?= llvm-objcopy-14
|
||||
|
||||
CC = clang-14
|
||||
|
||||
CFLAGS = -target riscv32-unknown-none-elf -march=rv32imc -mabi=ilp32 \
|
||||
-static -std=gnu99 -O2 -ffast-math -fno-common -fno-builtin-printf \
|
||||
-fno-builtin-putchar -nostdlib -mno-relax -Wall -flto
|
||||
|
||||
AS = clang-14
|
||||
ASFLAGS = -target riscv32-unknown-none-elf -march=rv32imc -mabi=ilp32 -mno-relax
|
||||
|
||||
ICE40_SIM_CELLS = $(shell yosys-config --datdir/ice40/cells_sim.v)
|
||||
|
||||
|
||||
# FPGA source files.
|
||||
TOP_SRC = $(P)/rtl/application_fpga.v
|
||||
|
||||
VERILATOR_TOP_SRC = $(P)/tb/application_fpga_vsim.v
|
||||
|
||||
VERILOG_SRCS = \
|
||||
$(P)/rtl/reset_gen.v \
|
||||
$(P)/rtl/ram.v \
|
||||
$(P)/rtl/rom.v \
|
||||
$(P)/core/picorv32/rtl/picorv32.v \
|
||||
$(P)/core/timer/rtl/timer_core.v \
|
||||
$(P)/core/timer/rtl/timer.v \
|
||||
$(P)/core/uds/rtl/uds.v \
|
||||
$(P)/core/touch_sense/rtl/touch_sense.v \
|
||||
$(P)/core/mta1/rtl/mta1.v \
|
||||
$(P)/core/uart/rtl/uart_core.v \
|
||||
$(P)/core/uart/rtl/uart_fifo.v \
|
||||
$(P)/core/uart/rtl/uart.v \
|
||||
$(P)/core/trng/rtl/firo.v \
|
||||
$(P)/core/trng/rtl/garo.v \
|
||||
$(P)/core/trng/rtl/figaro_core.v \
|
||||
$(P)/core/trng/rtl/figaro.v
|
||||
|
||||
FIRMWARE_DEPS = \
|
||||
$(P)/fw/mta1_mkdf_mem.h \
|
||||
$(P)/fw/mta1_mkdf/types.h \
|
||||
$(P)/fw/mta1_mkdf/lib.h \
|
||||
$(P)/fw/mta1_mkdf/proto.h
|
||||
|
||||
FIRMWARE_OBJS = \
|
||||
$(P)/fw/mta1_mkdf/main.o \
|
||||
$(P)/fw/mta1_mkdf/start.o \
|
||||
$(P)/fw/mta1_mkdf/proto.o \
|
||||
$(P)/fw/mta1_mkdf/lib.o \
|
||||
$(P)/fw/mta1_mkdf/blake2s/blake2s.o
|
||||
|
||||
TESTFW_OBJS = \
|
||||
$(P)/fw/testfw/main.o \
|
||||
$(P)/fw/mta1_mkdf/start.o \
|
||||
$(P)/fw/mta1_mkdf/proto.o \
|
||||
$(P)/fw/mta1_mkdf/lib.o
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# All: Complete build of HW and FW.
|
||||
#-------------------------------------------------------------------
|
||||
all: application_fpga.bin
|
||||
.PHONY: all
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# The size_mismatch target make sure that we don't end up with an
|
||||
# incorrect BRAM_FW_SIZE
|
||||
# -------------------------------------------------------------------
|
||||
size_mismatch: firmware.elf
|
||||
@test $$($(SIZE) $< | awk 'NR==2{print $$4}') -le $$(( 32 / 8 * $(BRAM_FW_SIZE) )) || \
|
||||
(echo "The 'BRAM_FW_SIZE' variable needs to be increased" && false)
|
||||
.PHONY: size_mismatch
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# Firmware generation.
|
||||
# Included in the bitstream.
|
||||
#-------------------------------------------------------------------
|
||||
LDFLAGS=-T $(P)/fw/mta1_mkdf/firmware.lds
|
||||
|
||||
$(FIRMWARE_OBJS): $(FIRMWARE_DEPS)
|
||||
$(TESTFW_OBJS): $(FIRMWARE_DEPS)
|
||||
|
||||
firmware.elf: $(FIRMWARE_OBJS) $(P)/fw/mta1_mkdf/firmware.lds
|
||||
$(CC) $(CFLAGS) $(FIRMWARE_OBJS) $(LDFLAGS) -o $@
|
||||
|
||||
testfw.elf: $(TESTFW_OBJS) $(P)/fw/mta1_mkdf/firmware.lds
|
||||
$(CC) $(CFLAGS) $(TESTFW_OBJS) $(LDFLAGS) -o $@
|
||||
|
||||
# Generate a fake BRAM file that will be filled in later after place-n-route
|
||||
bram_fw.hex:
|
||||
$(ICESTORM_PATH)icebram -v -g 32 $(BRAM_FW_SIZE) > $@
|
||||
|
||||
firmware.hex: firmware.bin size_mismatch
|
||||
python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@
|
||||
testfw.hex: testfw.bin size_mismatch
|
||||
python3 $(P)/tools/makehex/makehex.py $< $(BRAM_FW_SIZE) > $@
|
||||
|
||||
%.bin: %.elf
|
||||
$(SIZE) $<
|
||||
$(OBJCOPY) --input-target=elf32-littleriscv --output-target=binary $< $@
|
||||
chmod -x $@
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# Source linting.
|
||||
#-------------------------------------------------------------------
|
||||
LINT=verilator
|
||||
LINT_FLAGS = +1364-2001ext+ --lint-only -Wall -Wno-fatal -Wno-DECLFILENAME \
|
||||
--timescale 1ns/1ns -DNO_ICE40_DEFAULT_ASSIGNMENTS
|
||||
|
||||
lint: $(TOP_SRC) $(VERILOG_SRCS) $(ICE40_SIM_CELLS)
|
||||
$(LINT) $(LINT_FLAGS) \
|
||||
-DBRAM_FW_SIZE=$(BRAM_FW_SIZE) \
|
||||
-DFIRMWARE_HEX=\"$(P)/firmware.hex\" \
|
||||
-DUDS_HEX=\"$(P)/data/uds.hex\" \
|
||||
-DUDI_HEX=\"$(P)/data/udi.hex\" \
|
||||
--top-module application_fpga $^
|
||||
.PHONY: lint
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# Build Verilator compiled simulation for the design.
|
||||
#-------------------------------------------------------------------
|
||||
verilator: $(VERILATOR_TOP_SRC) $(VERILOG_SRCS) firmware.hex $(ICE40_SIM_CELLS) \
|
||||
$(P)/tb/application_fpga_verilator.cc
|
||||
verilator --timescale 1ns/1ns -DNO_ICE40_DEFAULT_ASSIGNMENTS \
|
||||
-Wall -Wno-COMBDLY -Wno-lint \
|
||||
-DBRAM_FW_SIZE=$(BRAM_FW_SIZE) \
|
||||
-DFIRMWARE_HEX=\"$(P)/firmware.hex\" \
|
||||
-DUDS_HEX=\"$(P)/data/uds.hex\" \
|
||||
-DUDI_HEX=\"$(P)/data/udi.hex\" \
|
||||
--cc --exe --Mdir verilated --top-module application_fpga \
|
||||
$(filter %.v, $^) $(filter %.cc, $^)
|
||||
make -C verilated -f Vapplication_fpga.mk
|
||||
.PHONY: verilator
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# Main FPGA build flow.
|
||||
# Synthesis. Place & Route. Bitstream generation.
|
||||
#-------------------------------------------------------------------
|
||||
synth.json: $(TOP_SRC) $(VERILOG_SRCS) bram_fw.hex
|
||||
$(YOSYS_PATH)yosys -v3 -l synth.log -DBRAM_FW_SIZE=$(BRAM_FW_SIZE) \
|
||||
-DFIRMWARE_HEX=\"$(P)/bram_fw.hex\" \
|
||||
-DUDS_HEX=\"$(P)/data/uds.hex\" \
|
||||
-DUDI_HEX=\"$(P)/data/udi.hex\" \
|
||||
-p 'synth_ice40 -dsp -top application_fpga -json $@; write_verilog -attr2comment synth.v' \
|
||||
$(filter %.v, $^)
|
||||
|
||||
application_fpga.asc: synth.json $(P)/data/$(PIN_FILE)
|
||||
$(NEXTPNR_PATH)nextpnr-ice40 --ignore-loops --up5k --package sg48 --json $< \
|
||||
--pcf $(P)/data/$(PIN_FILE) --asc $@
|
||||
|
||||
application_fpga.bin: application_fpga.asc bram_fw.hex firmware.hex
|
||||
$(ICESTORM_PATH)icebram -v bram_fw.hex firmware.hex < $< > $<.tmp
|
||||
$(ICESTORM_PATH)icepack $<.tmp $@
|
||||
@-$(RM) $<.tmp
|
||||
|
||||
application_fpga_testfw.bin: application_fpga.asc bram_fw.hex testfw.hex
|
||||
$(ICESTORM_PATH)icebram -v bram_fw.hex testfw.hex < $< > $<.tmp
|
||||
$(ICESTORM_PATH)icepack $<.tmp $@
|
||||
@-$(RM) $<.tmp
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# post-synthesis functional simulation.
|
||||
#-------------------------------------------------------------------
|
||||
synth_tb.vvp: $(P)/tb/tb_application_fpga.v synth.json
|
||||
iverilog -o $@ -s tb_application_fpga synth.v $(P)/tb/tb_application_fpga.v \
|
||||
-DNO_ICE40_DEFAULT_ASSIGNMENTS $(ICE40_SIM_CELLS)
|
||||
chmod -x $@
|
||||
|
||||
synth_sim: synth_tb.vvp
|
||||
vvp -N $<
|
||||
.PHONY: synth_sim
|
||||
|
||||
synth_sim_vcd: synth_tb.vvp
|
||||
vvp -N $< +vcd
|
||||
.PHONY: synth_sim_vcd
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# post-place and route functional simulation.
|
||||
#-------------------------------------------------------------------
|
||||
route.v: application_fpga.asc $(P)/data/$(PIN_FILE)
|
||||
icebox_vlog -L -n application_fpga -sp $(P)/data/$(PIN_FILE) $< > $@
|
||||
|
||||
route_tb.vvp: route.v tb/tb_application_fpga.v
|
||||
iverilog -o $@ -s tb_application_fpga $^ $(ICE40_SIM_CELLS)
|
||||
chmod -x $@
|
||||
|
||||
route_sim: route_tb.vvp
|
||||
vvp -N $<
|
||||
.PHONY: route_sim
|
||||
|
||||
route_sim_vcd: route_tb.vvp
|
||||
vvp -N $< +vcd
|
||||
.PHONY: route_sim_vcd
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# FPGA device programming.
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
prog_flash: application_fpga.bin
|
||||
sudo tillitis-iceprog $<
|
||||
.PHONY: prog_flash
|
||||
|
||||
prog_flash_testfw: application_fpga_testfw.bin
|
||||
sudo tillitis-iceprog $<
|
||||
.PHONY: prog_flash_testfw
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# Post build analysis.
|
||||
#-------------------------------------------------------------------
|
||||
timing: application_fpga.asc $(P)/data/$(PIN_FILE)
|
||||
$(ICESTORM_PATH)icetime -c 12 -tmd up5k -P sg48 -p $(P)/data/$(PIN_FILE) -t $<
|
||||
|
||||
view: tb_application_fpga_vcd
|
||||
gtkwave $< application_fpga.gtkw
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# Cleanup.
|
||||
#-------------------------------------------------------------------
|
||||
clean: clean_fw
|
||||
rm -f bram_fw.hex
|
||||
rm -f synth.{log,v,json} route.v application_fpga.{asc,bin,vcd} application_fpga_testfw.bin
|
||||
rm -f tb_application_fpga.vvp synth_tb.vvp route_tb.vvp
|
||||
rm -f *.vcd
|
||||
rm -rf verilated
|
||||
rm -f tools/tpt/*.hex
|
||||
rm -rf tools/tpt/__pycache__
|
||||
.PHONY: clean
|
||||
|
||||
clean_fw:
|
||||
rm -f firmware.{elf,elf.map,bin,hex}
|
||||
rm -f $(FIRMWARE_OBJS)
|
||||
rm -f testfw.{elf,elf.map,bin,hex}
|
||||
rm -f $(TESTFW_OBJS)
|
||||
.PHONY: clean_fw
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# Display info about targets.
|
||||
#-------------------------------------------------------------------
|
||||
help:
|
||||
@echo ""
|
||||
@echo "Build system for application_fpga FPGA design and firmware."
|
||||
@echo ""
|
||||
@echo "Supported targets:"
|
||||
@echo "------------------"
|
||||
@echo "all Build all targets."
|
||||
@echo "firmware.elf Build firmware ELF file."
|
||||
@echo "firmware.hex Build firmware converted to hex, to be included in bitstream."
|
||||
@echo "bram_fw.hex Build a fake BRAM file that will be filled in later after place-n-route."
|
||||
@echo "verilator Build Verilator simulation program"
|
||||
@echo "lint Run lint on Verilog source files."
|
||||
@echo "prog_flash Program device flash with FGPA bitstream including firmware (using the RPi Pico-based programmer)."
|
||||
@echo "prog_flash_testfw Program device flash as above, but with testfw."
|
||||
@echo "clean Delete all generated files."
|
||||
@echo "clean_fw Delete only generated files for firmware. Useful for fw devs."
|
||||
|
||||
#=======================================================================
|
||||
# EOF Makefile
|
||||
#=======================================================================
|
4
hw/application_fpga/core/mta1/README.md
Normal file
4
hw/application_fpga/core/mta1/README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
# mta1
|
||||
|
||||
## Introduction
|
||||
Top level core that provides chip info, debug support and chip level control.
|
326
hw/application_fpga/core/mta1/rtl/mta1.v
Normal file
326
hw/application_fpga/core/mta1/rtl/mta1.v
Normal file
|
@ -0,0 +1,326 @@
|
|||
//======================================================================
|
||||
//
|
||||
// mta1.v
|
||||
// ------
|
||||
// Top level information, debug and control core for the mta1 design.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module mta1(
|
||||
input wire clk,
|
||||
input wire reset_n,
|
||||
|
||||
output wire fw_app_mode,
|
||||
|
||||
output wire led_r,
|
||||
output wire led_g,
|
||||
output wire led_b,
|
||||
|
||||
input wire gpio1,
|
||||
input wire gpio2,
|
||||
output wire gpio3,
|
||||
output wire gpio4,
|
||||
|
||||
input wire cs,
|
||||
input wire we,
|
||||
|
||||
input wire [7 : 0] address,
|
||||
input wire [31 : 0] write_data,
|
||||
output wire [31 : 0] read_data,
|
||||
output wire ready
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Internal constant and parameter definitions.
|
||||
//----------------------------------------------------------------
|
||||
localparam ADDR_NAME0 = 8'h00;
|
||||
localparam ADDR_NAME1 = 8'h01;
|
||||
localparam ADDR_VERSION = 8'h02;
|
||||
|
||||
localparam ADDR_SWITCH_APP = 8'h08;
|
||||
|
||||
localparam ADDR_LED = 8'h09;
|
||||
localparam LED_R_BIT = 2;
|
||||
localparam LED_G_BIT = 1;
|
||||
localparam LED_B_BIT = 0;
|
||||
|
||||
localparam ADDR_GPIO = 8'h0a;
|
||||
localparam GPIO1_BIT = 0;
|
||||
localparam GPIO2_BIT = 1;
|
||||
localparam GPIO3_BIT = 2;
|
||||
localparam GPIO4_BIT = 3;
|
||||
|
||||
localparam ADDR_APP_START = 8'h0c;
|
||||
localparam ADDR_APP_SIZE = 8'h0d;
|
||||
|
||||
localparam ADDR_DEBUG = 8'h10;
|
||||
|
||||
localparam ADDR_CDI_FIRST = 8'h20;
|
||||
localparam ADDR_CDI_LAST = 8'h27;
|
||||
|
||||
localparam ADDR_UDI_FIRST = 8'h30;
|
||||
localparam ADDR_UDI_LAST = 8'h31;
|
||||
|
||||
localparam MTA1_NAME0 = 32'h6d746131; // "mta1"
|
||||
localparam MTA1_NAME1 = 32'h6d6b6466; // "mkdf"
|
||||
localparam MTA1_VERSION = 32'h00000004;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Registers including update variables and write enable.
|
||||
//----------------------------------------------------------------
|
||||
reg [31 : 0] cdi_mem [0 : 7];
|
||||
reg [31 : 0] cdi_mem_we;
|
||||
|
||||
reg [31 : 0] udi_mem [0 : 1];
|
||||
initial $readmemh(`UDI_HEX, udi_mem);
|
||||
|
||||
reg switch_app_reg;
|
||||
reg switch_app_we;
|
||||
|
||||
reg [2 : 0] led_reg;
|
||||
reg led_we;
|
||||
|
||||
reg [1 : 0] gpio1_reg;
|
||||
reg [1 : 0] gpio2_reg;
|
||||
reg gpio3_reg;
|
||||
reg gpio3_we;
|
||||
reg gpio4_reg;
|
||||
reg gpio4_we;
|
||||
|
||||
reg [31 : 0] app_start_reg;
|
||||
reg app_start_we;
|
||||
|
||||
reg [31 : 0] app_size_reg;
|
||||
reg app_size_we;
|
||||
|
||||
reg [31 : 0] debug_reg;
|
||||
reg debug_we;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Wires.
|
||||
//----------------------------------------------------------------
|
||||
/* verilator lint_off UNOPTFLAT */
|
||||
reg [31 : 0] tmp_read_data;
|
||||
reg tmp_ready;
|
||||
/* verilator lint_on UNOPTFLAT */
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Concurrent connectivity for ports etc.
|
||||
//----------------------------------------------------------------
|
||||
assign read_data = tmp_read_data;
|
||||
assign ready = tmp_ready;
|
||||
|
||||
assign fw_app_mode = switch_app_reg;
|
||||
|
||||
assign gpio3 = gpio3_reg;
|
||||
assign gpio4 = gpio4_reg;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Module instance.
|
||||
//----------------------------------------------------------------
|
||||
SB_RGBA_DRV #(
|
||||
.CURRENT_MODE("0b1"), // half-current mode
|
||||
.RGB0_CURRENT("0b000001"), // 2 mA
|
||||
.RGB1_CURRENT("0b000001"), // 2 mA
|
||||
.RGB2_CURRENT("0b000001") // 2 mA
|
||||
) RGBA_DRV (
|
||||
.RGB0(led_r),
|
||||
.RGB1(led_g),
|
||||
.RGB2(led_b),
|
||||
.RGBLEDEN(1'h1),
|
||||
.RGB0PWM(led_reg[LED_R_BIT]),
|
||||
.RGB1PWM(led_reg[LED_G_BIT]),
|
||||
.RGB2PWM(led_reg[LED_B_BIT]),
|
||||
.CURREN(1'b1)
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// reg_update
|
||||
//----------------------------------------------------------------
|
||||
always @ (posedge clk)
|
||||
begin : reg_update
|
||||
if (!reset_n) begin
|
||||
switch_app_reg <= 1'h0;
|
||||
led_reg <= 3'h6;
|
||||
gpio1_reg <= 2'h0;
|
||||
gpio2_reg <= 2'h0;
|
||||
gpio3_reg <= 1'h0;
|
||||
gpio4_reg <= 1'h0;
|
||||
app_start_reg <= 32'h0;
|
||||
app_size_reg <= 32'h0;
|
||||
debug_reg <= 32'h0;
|
||||
cdi_mem[0] <= 32'h0;
|
||||
cdi_mem[1] <= 32'h0;
|
||||
cdi_mem[2] <= 32'h0;
|
||||
cdi_mem[3] <= 32'h0;
|
||||
cdi_mem[4] <= 32'h0;
|
||||
cdi_mem[5] <= 32'h0;
|
||||
cdi_mem[6] <= 32'h0;
|
||||
cdi_mem[7] <= 32'h0;
|
||||
end
|
||||
|
||||
else begin
|
||||
gpio1_reg[0] <= gpio1;
|
||||
gpio1_reg[1] <= gpio1_reg[0];
|
||||
|
||||
gpio2_reg[0] <= gpio2;
|
||||
gpio2_reg[1] <= gpio2_reg[0];
|
||||
|
||||
if (switch_app_we) begin
|
||||
switch_app_reg <= 1'h1;
|
||||
end
|
||||
|
||||
if (led_we) begin
|
||||
led_reg <= write_data[2 : 0];
|
||||
end
|
||||
|
||||
if (gpio3_we) begin
|
||||
gpio3_reg <= write_data[GPIO3_BIT];
|
||||
end
|
||||
|
||||
if (gpio4_we) begin
|
||||
gpio4_reg <= write_data[GPIO4_BIT];
|
||||
end
|
||||
|
||||
if (app_start_we) begin
|
||||
app_start_reg <= write_data;
|
||||
end
|
||||
|
||||
if (app_size_we) begin
|
||||
app_size_reg <= write_data;
|
||||
end
|
||||
|
||||
if (debug_we) begin
|
||||
debug_reg <= write_data;
|
||||
end
|
||||
|
||||
if (cdi_mem_we) begin
|
||||
cdi_mem[address[2 : 0]] <= write_data;
|
||||
end
|
||||
end
|
||||
end // reg_update
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// api
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin : api
|
||||
switch_app_we = 1'h0;
|
||||
led_we = 1'h0;
|
||||
gpio3_we = 1'h0;
|
||||
gpio4_we = 1'h0;
|
||||
app_start_we = 1'h0;
|
||||
app_size_we = 1'h0;
|
||||
debug_we = 1'h0;
|
||||
cdi_mem_we = 1'h0;
|
||||
cdi_mem_we = 1'h0;
|
||||
tmp_read_data = 32'h00000000;
|
||||
tmp_ready = 1'h0;
|
||||
|
||||
if (cs) begin
|
||||
tmp_ready = 1'h1;
|
||||
if (we) begin
|
||||
if (address == ADDR_SWITCH_APP) begin
|
||||
switch_app_we = 1'h1;
|
||||
end
|
||||
|
||||
if (address == ADDR_LED) begin
|
||||
led_we = 1'h1;
|
||||
end
|
||||
|
||||
if (address == ADDR_GPIO) begin
|
||||
gpio3_we = 1'h1;
|
||||
gpio4_we = 1'h1;
|
||||
end
|
||||
|
||||
if (address == ADDR_APP_START) begin
|
||||
if (!switch_app_reg) begin
|
||||
app_start_we = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
if (address == ADDR_APP_SIZE) begin
|
||||
if (!switch_app_reg) begin
|
||||
app_size_we = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
if (address == ADDR_DEBUG) begin
|
||||
debug_we = 1'h1;
|
||||
end
|
||||
|
||||
if ((address >= ADDR_CDI_FIRST) && (address <= ADDR_CDI_LAST)) begin
|
||||
if (!switch_app_reg) begin
|
||||
cdi_mem_we = 1'h1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
else begin
|
||||
if (address == ADDR_NAME0) begin
|
||||
tmp_read_data = MTA1_NAME0;
|
||||
end
|
||||
|
||||
if (address == ADDR_NAME1) begin
|
||||
tmp_read_data = MTA1_NAME1;
|
||||
end
|
||||
|
||||
if (address == ADDR_VERSION) begin
|
||||
tmp_read_data = MTA1_VERSION;
|
||||
end
|
||||
|
||||
if (address == ADDR_SWITCH_APP) begin
|
||||
tmp_read_data = {32{switch_app_reg}};
|
||||
end
|
||||
|
||||
if (address == ADDR_LED) begin
|
||||
tmp_read_data = {29'h0, led_reg};
|
||||
end
|
||||
|
||||
if (address == ADDR_GPIO) begin
|
||||
tmp_read_data = {28'h0, gpio4_reg, gpio3_reg,
|
||||
gpio2_reg[1], gpio1_reg[1]};
|
||||
end
|
||||
|
||||
if (address == ADDR_APP_START) begin
|
||||
tmp_read_data = app_start_reg;
|
||||
end
|
||||
|
||||
if (address == ADDR_APP_SIZE) begin
|
||||
tmp_read_data = app_size_reg;
|
||||
end
|
||||
|
||||
if (address == ADDR_DEBUG) begin
|
||||
tmp_read_data = debug_reg;
|
||||
end
|
||||
|
||||
if ((address >= ADDR_CDI_FIRST) && (address <= ADDR_CDI_LAST)) begin
|
||||
tmp_read_data = cdi_mem[address[2 : 0]];
|
||||
end
|
||||
|
||||
if ((address >= ADDR_UDI_FIRST) && (address <= ADDR_UDI_LAST)) begin
|
||||
tmp_read_data = udi_mem[address[0]];
|
||||
end
|
||||
end
|
||||
end
|
||||
end // api
|
||||
|
||||
endmodule // mta1
|
||||
|
||||
//======================================================================
|
||||
// EOF mta1.v
|
||||
//======================================================================
|
15
hw/application_fpga/core/picorv32/LICENSE
Normal file
15
hw/application_fpga/core/picorv32/LICENSE
Normal file
|
@ -0,0 +1,15 @@
|
|||
ISC License
|
||||
|
||||
Copyright (C) 2015 - 2021 Claire Xenia Wolf <claire@yosyshq.com>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
4
hw/application_fpga/core/picorv32/README.md
Normal file
4
hw/application_fpga/core/picorv32/README.md
Normal file
|
@ -0,0 +1,4 @@
|
|||
# PicoRV32 - A Size-Optimized RISC-V CPU
|
||||
|
||||
This is a local copy of the
|
||||
[PicoRV32](https://github.com/cliffordwolf/picorv32) RISC-V CPU core.
|
3044
hw/application_fpga/core/picorv32/rtl/picorv32.v
Normal file
3044
hw/application_fpga/core/picorv32/rtl/picorv32.v
Normal file
File diff suppressed because it is too large
Load diff
5
hw/application_fpga/core/timer/README.md
Normal file
5
hw/application_fpga/core/timer/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# timer
|
||||
A simple timer with prescaler written in Verilog.
|
||||
|
||||
## Introduction
|
||||
This core implements a simple timer with a prescaler. The purpose of the prescaler is to more easily time durations rather than cycles. If for example setting the timer to the clock frequency, the timer can cound seconds.
|
194
hw/application_fpga/core/timer/rtl/timer.v
Normal file
194
hw/application_fpga/core/timer/rtl/timer.v
Normal file
|
@ -0,0 +1,194 @@
|
|||
//======================================================================
|
||||
//
|
||||
// timer.v
|
||||
// --------
|
||||
// Top level wrapper for the timer core.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module timer(
|
||||
input wire clk,
|
||||
input wire reset_n,
|
||||
|
||||
input wire cs,
|
||||
input wire we,
|
||||
|
||||
input wire [7 : 0] address,
|
||||
input wire [31 : 0] write_data,
|
||||
output wire [31 : 0] read_data,
|
||||
output wire ready
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Internal constant and parameter definitions.
|
||||
//----------------------------------------------------------------
|
||||
localparam ADDR_NAME0 = 8'h00;
|
||||
localparam ADDR_NAME1 = 8'h01;
|
||||
localparam ADDR_VERSION = 8'h02;
|
||||
|
||||
localparam ADDR_CTRL = 8'h08;
|
||||
localparam CTRL_START_BIT = 0;
|
||||
localparam CTRL_STOP_BIT = 1;
|
||||
|
||||
localparam ADDR_STATUS = 8'h09;
|
||||
localparam STATUS_READY_BIT = 0;
|
||||
|
||||
localparam ADDR_PRESCALER = 8'h0a;
|
||||
localparam ADDR_TIMER = 8'h0b;
|
||||
|
||||
localparam CORE_NAME0 = 32'h74696d65; // "time"
|
||||
localparam CORE_NAME1 = 32'h72202020; // "r "
|
||||
localparam CORE_VERSION = 32'h00000003;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Registers including update variables and write enable.
|
||||
//----------------------------------------------------------------
|
||||
reg [31 : 0] prescaler_reg;
|
||||
reg prescaler_we;
|
||||
|
||||
reg [31 : 0] timer_reg;
|
||||
reg timer_we;
|
||||
|
||||
reg start_reg;
|
||||
reg start_new;
|
||||
|
||||
reg stop_reg;
|
||||
reg stop_new;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Wires.
|
||||
//----------------------------------------------------------------
|
||||
reg [31 : 0] tmp_read_data;
|
||||
reg tmp_ready;
|
||||
|
||||
wire core_ready;
|
||||
wire [31 : 0] core_curr_prescaler;
|
||||
wire [31 : 0] core_curr_timer;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Concurrent connectivity for ports etc.
|
||||
//----------------------------------------------------------------
|
||||
assign read_data = tmp_read_data;
|
||||
assign ready = tmp_ready;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// core instantiation.
|
||||
//----------------------------------------------------------------
|
||||
timer_core core(
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
.prescaler_value(prescaler_reg),
|
||||
.timer_value(timer_reg),
|
||||
.start(start_reg),
|
||||
.stop(stop_reg),
|
||||
.curr_prescaler(core_curr_prescaler),
|
||||
.curr_timer(core_curr_timer),
|
||||
.ready(core_ready)
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// reg_update
|
||||
//----------------------------------------------------------------
|
||||
always @ (posedge clk)
|
||||
begin : reg_update
|
||||
if (!reset_n) begin
|
||||
start_reg <= 1'h0;
|
||||
stop_reg <= 1'h0;
|
||||
prescaler_reg <= 32'h0;
|
||||
timer_reg <= 32'h0;
|
||||
end
|
||||
|
||||
else begin
|
||||
start_reg <= start_new;
|
||||
stop_reg <= stop_new;
|
||||
|
||||
if (prescaler_we) begin
|
||||
prescaler_reg <= write_data;
|
||||
end
|
||||
|
||||
if (timer_we) begin
|
||||
timer_reg <= write_data;
|
||||
end
|
||||
end
|
||||
end // reg_update
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// api
|
||||
//
|
||||
// The interface command decoding logic.
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin : api
|
||||
start_new = 1'h0;
|
||||
stop_new = 1'h0;
|
||||
prescaler_we = 1'h0;
|
||||
timer_we = 1'h0;
|
||||
tmp_read_data = 32'h0;
|
||||
tmp_ready = 1'h0;
|
||||
|
||||
if (cs) begin
|
||||
tmp_ready = 1'h1;
|
||||
|
||||
if (we) begin
|
||||
if (address == ADDR_CTRL) begin
|
||||
start_new = write_data[CTRL_START_BIT];
|
||||
stop_new = write_data[CTRL_STOP_BIT];
|
||||
end
|
||||
|
||||
if (core_ready) begin
|
||||
if (address == ADDR_PRESCALER) begin
|
||||
prescaler_we = 1'h1;
|
||||
end
|
||||
|
||||
if (address == ADDR_TIMER) begin
|
||||
timer_we = 1'h1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
else begin
|
||||
if (address == ADDR_NAME0) begin
|
||||
tmp_read_data = CORE_NAME0;
|
||||
end
|
||||
|
||||
if (address == ADDR_NAME1) begin
|
||||
tmp_read_data = CORE_NAME1;
|
||||
end
|
||||
|
||||
if (address == ADDR_VERSION) begin
|
||||
tmp_read_data = CORE_VERSION;
|
||||
end
|
||||
|
||||
if (address == ADDR_STATUS) begin
|
||||
tmp_read_data = {31'h0, core_ready};
|
||||
end
|
||||
|
||||
if (address == ADDR_PRESCALER) begin
|
||||
tmp_read_data = core_curr_prescaler;
|
||||
end
|
||||
|
||||
if (address == ADDR_TIMER) begin
|
||||
tmp_read_data = core_curr_timer;
|
||||
end
|
||||
end
|
||||
end
|
||||
end // addr_decoder
|
||||
endmodule // timer
|
||||
|
||||
//======================================================================
|
||||
// EOF timer.v
|
||||
//======================================================================
|
225
hw/application_fpga/core/timer/rtl/timer_core.v
Normal file
225
hw/application_fpga/core/timer/rtl/timer_core.v
Normal file
|
@ -0,0 +1,225 @@
|
|||
//======================================================================
|
||||
//
|
||||
// timer_core.v
|
||||
// ------------
|
||||
// timer core.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module timer_core(
|
||||
input wire clk,
|
||||
input wire reset_n,
|
||||
|
||||
input wire [31 : 0] prescaler_value,
|
||||
input wire [31 : 0] timer_value,
|
||||
input wire start,
|
||||
input wire stop,
|
||||
|
||||
output wire [31 : 0] curr_prescaler,
|
||||
output wire [31 : 0] curr_timer,
|
||||
|
||||
output wire ready
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Internal constant and parameter definitions.
|
||||
//----------------------------------------------------------------
|
||||
localparam CTRL_IDLE = 2'h0;
|
||||
localparam CTRL_PRESCALER = 2'h1;
|
||||
localparam CTRL_TIMER = 2'h2;
|
||||
localparam CTRL_DONE = 2'h3;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Registers including update variables and write enable.
|
||||
//----------------------------------------------------------------
|
||||
reg ready_reg;
|
||||
reg ready_new;
|
||||
reg ready_we;
|
||||
|
||||
reg [31 : 0] prescaler_reg;
|
||||
reg [31 : 0] prescaler_new;
|
||||
reg prescaler_we;
|
||||
reg prescaler_set;
|
||||
reg prescaler_dec;
|
||||
|
||||
reg [31 : 0] timer_reg;
|
||||
reg [31 : 0] timer_new;
|
||||
reg timer_we;
|
||||
reg timer_set;
|
||||
reg timer_dec;
|
||||
|
||||
reg [1 : 0] core_ctrl_reg;
|
||||
reg [1 : 0] core_ctrl_new;
|
||||
reg core_ctrl_we;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Concurrent connectivity for ports etc.
|
||||
//----------------------------------------------------------------
|
||||
assign curr_prescaler = prescaler_reg;
|
||||
assign curr_timer = timer_reg;
|
||||
assign ready = ready_reg;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// reg_update
|
||||
//----------------------------------------------------------------
|
||||
always @ (posedge clk)
|
||||
begin: reg_update
|
||||
if (!reset_n)
|
||||
begin
|
||||
ready_reg <= 1'h1;
|
||||
prescaler_reg <= 32'h0;
|
||||
timer_reg <= 32'h0;
|
||||
core_ctrl_reg <= CTRL_IDLE;
|
||||
end
|
||||
else
|
||||
begin
|
||||
if (ready_we) begin
|
||||
ready_reg <= ready_new;
|
||||
end
|
||||
|
||||
if (prescaler_we) begin
|
||||
prescaler_reg <= prescaler_new;
|
||||
end
|
||||
|
||||
if (timer_we) begin
|
||||
timer_reg <= timer_new;
|
||||
end
|
||||
|
||||
if (core_ctrl_we) begin
|
||||
core_ctrl_reg <= core_ctrl_new;
|
||||
end
|
||||
end
|
||||
end // reg_update
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// prescaler_ctr
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin : prescaler_ctr
|
||||
prescaler_new = 32'h0;
|
||||
prescaler_we = 1'h0;
|
||||
|
||||
if (prescaler_set) begin
|
||||
prescaler_new = prescaler_value;
|
||||
prescaler_we = 1'h1;
|
||||
end
|
||||
else if (prescaler_dec) begin
|
||||
prescaler_new = prescaler_reg - 1'h1;
|
||||
prescaler_we = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// timer_ctr
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin : timer_ctr
|
||||
timer_new = 32'h0;
|
||||
timer_we = 1'h0;
|
||||
|
||||
if (timer_set) begin
|
||||
timer_new = timer_value;
|
||||
timer_we = 1'h1;
|
||||
end
|
||||
else if (timer_dec) begin
|
||||
timer_new = timer_reg - 1'h1;
|
||||
timer_we = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Core control FSM.
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin : core_ctrl
|
||||
ready_new = 1'h0;
|
||||
ready_we = 1'h0;
|
||||
prescaler_set = 1'h0;
|
||||
prescaler_dec = 1'h0;
|
||||
timer_set = 1'h0;
|
||||
timer_dec = 1'h0;
|
||||
core_ctrl_new = CTRL_IDLE;
|
||||
core_ctrl_we = 1'h0;
|
||||
|
||||
case (core_ctrl_reg)
|
||||
CTRL_IDLE: begin
|
||||
if (start)
|
||||
begin
|
||||
ready_new = 1'h0;
|
||||
ready_we = 1'h1;
|
||||
prescaler_set = 1'h1;
|
||||
timer_set = 1'h1;
|
||||
core_ctrl_new = CTRL_PRESCALER;
|
||||
core_ctrl_we = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
CTRL_PRESCALER: begin
|
||||
if (stop) begin
|
||||
core_ctrl_new = CTRL_DONE;
|
||||
core_ctrl_we = 1'h1;
|
||||
end
|
||||
else begin
|
||||
if (prescaler_reg == 0) begin
|
||||
core_ctrl_new = CTRL_TIMER;
|
||||
core_ctrl_we = 1'h1;
|
||||
end
|
||||
else begin
|
||||
prescaler_dec = 1'h1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
CTRL_TIMER: begin
|
||||
if (stop) begin
|
||||
core_ctrl_new = CTRL_DONE;
|
||||
core_ctrl_we = 1'h1;
|
||||
end
|
||||
else begin
|
||||
if (timer_reg == 0) begin
|
||||
core_ctrl_new = CTRL_DONE;
|
||||
core_ctrl_we = 1'h1;
|
||||
end
|
||||
else begin
|
||||
prescaler_set = 1'h1;
|
||||
timer_dec = 1'h1;
|
||||
core_ctrl_new = CTRL_PRESCALER;
|
||||
core_ctrl_we = 1'h1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
CTRL_DONE: begin
|
||||
ready_new = 1'h1;
|
||||
ready_we = 1'h1;
|
||||
core_ctrl_new = CTRL_IDLE;
|
||||
core_ctrl_we = 1'h1;
|
||||
end
|
||||
|
||||
default: begin
|
||||
end
|
||||
endcase // case (core_ctrl_reg)
|
||||
end // core_ctrl
|
||||
|
||||
endmodule // timer_core
|
||||
|
||||
//======================================================================
|
||||
// EOF timer_core.v
|
||||
//======================================================================
|
293
hw/application_fpga/core/timer/tb/tb_timer.v
Normal file
293
hw/application_fpga/core/timer/tb/tb_timer.v
Normal file
|
@ -0,0 +1,293 @@
|
|||
//======================================================================
|
||||
//
|
||||
// tb_timer.v
|
||||
// -----------
|
||||
// Testbench for the timer top level wrapper.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_timer();
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Internal constant and parameter definitions.
|
||||
//----------------------------------------------------------------
|
||||
parameter DEBUG = 0;
|
||||
parameter DUMP_WAIT = 0;
|
||||
|
||||
parameter CLK_HALF_PERIOD = 1;
|
||||
parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD;
|
||||
|
||||
localparam ADDR_NAME0 = 8'h00;
|
||||
localparam ADDR_NAME1 = 8'h01;
|
||||
localparam ADDR_VERSION = 8'h02;
|
||||
|
||||
localparam ADDR_CTRL = 8'h08;
|
||||
localparam CTRL_NEXT_BIT = 0;
|
||||
|
||||
localparam ADDR_STATUS = 8'h09;
|
||||
localparam STATUS_READY_BIT = 0;
|
||||
|
||||
localparam ADDR_CONFIG = 8'h0a;
|
||||
localparam CONFIG_ENCDEC_BIT = 0;
|
||||
|
||||
localparam ADDR_KEY0 = 8'h10;
|
||||
localparam ADDR_KEY1 = 8'h11;
|
||||
localparam ADDR_KEY2 = 8'h12;
|
||||
localparam ADDR_KEY3 = 8'h13;
|
||||
|
||||
localparam ADDR_BLOCK0 = 8'h20;
|
||||
localparam ADDR_BLOCK1 = 8'h21;
|
||||
|
||||
localparam ADDR_RESULT0 = 8'h30;
|
||||
localparam ADDR_RESULT1 = 8'h31;
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Register and Wire declarations.
|
||||
//----------------------------------------------------------------
|
||||
reg [31 : 0] cycle_ctr;
|
||||
reg [31 : 0] error_ctr;
|
||||
reg [31 : 0] tc_ctr;
|
||||
reg tb_monitor;
|
||||
|
||||
reg tb_clk;
|
||||
reg tb_reset_n;
|
||||
reg tb_cs;
|
||||
reg tb_we;
|
||||
reg [7 : 0] tb_address;
|
||||
reg [31 : 0] tb_write_data;
|
||||
wire [31 : 0] tb_read_data;
|
||||
|
||||
reg [31 : 0] read_data;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Device Under Test.
|
||||
//----------------------------------------------------------------
|
||||
timer dut(
|
||||
.clk(tb_clk),
|
||||
.reset_n(tb_reset_n),
|
||||
|
||||
.cs(tb_cs),
|
||||
.we(tb_we),
|
||||
|
||||
.address(tb_address),
|
||||
.write_data(tb_write_data),
|
||||
.read_data(tb_read_data)
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// clk_gen
|
||||
//
|
||||
// Always running clock generator process.
|
||||
//----------------------------------------------------------------
|
||||
always
|
||||
begin : clk_gen
|
||||
#CLK_HALF_PERIOD;
|
||||
tb_clk = !tb_clk;
|
||||
end // clk_gen
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// sys_monitor()
|
||||
//
|
||||
// An always running process that creates a cycle counter and
|
||||
// conditionally displays information about the DUT.
|
||||
//----------------------------------------------------------------
|
||||
always
|
||||
begin : sys_monitor
|
||||
cycle_ctr = cycle_ctr + 1;
|
||||
#(CLK_PERIOD);
|
||||
if (tb_monitor)
|
||||
begin
|
||||
dump_dut_state();
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// dump_dut_state()
|
||||
//
|
||||
// Dump the state of the dump when needed.
|
||||
//----------------------------------------------------------------
|
||||
task dump_dut_state;
|
||||
begin
|
||||
$display("State of DUT");
|
||||
$display("------------");
|
||||
$display("Cycle: %08d", cycle_ctr);
|
||||
$display("");
|
||||
end
|
||||
endtask // dump_dut_state
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// reset_dut()
|
||||
//
|
||||
// Toggle reset to put the DUT into a well known state.
|
||||
//----------------------------------------------------------------
|
||||
task reset_dut;
|
||||
begin
|
||||
$display("--- Toggle reset.");
|
||||
tb_reset_n = 0;
|
||||
#(2 * CLK_PERIOD);
|
||||
tb_reset_n = 1;
|
||||
end
|
||||
endtask // reset_dut
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// display_test_result()
|
||||
//
|
||||
// Display the accumulated test results.
|
||||
//----------------------------------------------------------------
|
||||
task display_test_result;
|
||||
begin
|
||||
if (error_ctr == 0)
|
||||
begin
|
||||
$display("--- All %02d test cases completed successfully", tc_ctr);
|
||||
end
|
||||
else
|
||||
begin
|
||||
$display("--- %02d tests completed - %02d test cases did not complete successfully.",
|
||||
tc_ctr, error_ctr);
|
||||
end
|
||||
end
|
||||
endtask // display_test_result
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// init_sim()
|
||||
//
|
||||
// Initialize all counters and testbed functionality as well
|
||||
// as setting the DUT inputs to defined values.
|
||||
//----------------------------------------------------------------
|
||||
task init_sim;
|
||||
begin
|
||||
cycle_ctr = 0;
|
||||
error_ctr = 0;
|
||||
tc_ctr = 0;
|
||||
tb_monitor = 0;
|
||||
|
||||
tb_clk = 1'h0;
|
||||
tb_reset_n = 1'h1;
|
||||
tb_cs = 1'h0;
|
||||
tb_we = 1'h0;
|
||||
tb_address = 8'h0;
|
||||
tb_write_data = 32'h0;
|
||||
end
|
||||
endtask // init_sim
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// write_word()
|
||||
//
|
||||
// Write the given word to the DUT using the DUT interface.
|
||||
//----------------------------------------------------------------
|
||||
task write_word(input [11 : 0] address,
|
||||
input [31 : 0] word);
|
||||
begin
|
||||
if (DEBUG)
|
||||
begin
|
||||
$display("--- Writing 0x%08x to 0x%02x.", word, address);
|
||||
$display("");
|
||||
end
|
||||
|
||||
tb_address = address;
|
||||
tb_write_data = word;
|
||||
tb_cs = 1;
|
||||
tb_we = 1;
|
||||
#(2 * CLK_PERIOD);
|
||||
tb_cs = 0;
|
||||
tb_we = 0;
|
||||
end
|
||||
endtask // write_word
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// read_word()
|
||||
//
|
||||
// Read a data word from the given address in the DUT.
|
||||
// the word read will be available in the global variable
|
||||
// read_data.
|
||||
//----------------------------------------------------------------
|
||||
task read_word(input [11 : 0] address);
|
||||
begin
|
||||
tb_address = address;
|
||||
tb_cs = 1;
|
||||
tb_we = 0;
|
||||
#(CLK_PERIOD);
|
||||
read_data = tb_read_data;
|
||||
tb_cs = 0;
|
||||
|
||||
if (DEBUG)
|
||||
begin
|
||||
$display("--- Reading 0x%08x from 0x%02x.", read_data, address);
|
||||
$display("");
|
||||
end
|
||||
end
|
||||
endtask // read_word
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// wait_ready()
|
||||
//
|
||||
// Wait for the ready flag to be set in dut.
|
||||
//----------------------------------------------------------------
|
||||
task wait_ready;
|
||||
begin : wready
|
||||
read_word(ADDR_STATUS);
|
||||
while (read_data == 0)
|
||||
read_word(ADDR_STATUS);
|
||||
end
|
||||
endtask // wait_ready
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// test1()
|
||||
//----------------------------------------------------------------
|
||||
task test1;
|
||||
begin
|
||||
tc_ctr = tc_ctr + 1;
|
||||
|
||||
$display("");
|
||||
$display("--- test1: started.");
|
||||
|
||||
$display("--- test1: completed.");
|
||||
$display("");
|
||||
end
|
||||
endtask // tes1
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// timer_test
|
||||
//----------------------------------------------------------------
|
||||
initial
|
||||
begin : timer_test
|
||||
$display("");
|
||||
$display(" -= Testbench for timer started =-");
|
||||
$display(" =============================");
|
||||
$display("");
|
||||
|
||||
init_sim();
|
||||
reset_dut();
|
||||
test1();
|
||||
|
||||
display_test_result();
|
||||
$display("");
|
||||
$display(" -= Testbench for timer started =-");
|
||||
$display(" =============================");
|
||||
$display("");
|
||||
$finish;
|
||||
end // timer_test
|
||||
endmodule // tb_timer
|
||||
|
||||
//======================================================================
|
||||
// EOF tb_timer.v
|
||||
//======================================================================
|
247
hw/application_fpga/core/timer/tb/tb_timer_core.v
Normal file
247
hw/application_fpga/core/timer/tb/tb_timer_core.v
Normal file
|
@ -0,0 +1,247 @@
|
|||
//======================================================================
|
||||
//
|
||||
// tb_timer_core.v
|
||||
// --------------
|
||||
// Testbench for the timer core.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_timer_core();
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Internal constant and parameter definitions.
|
||||
//----------------------------------------------------------------
|
||||
parameter DEBUG = 0;
|
||||
parameter DUMP_WAIT = 0;
|
||||
|
||||
parameter CLK_HALF_PERIOD = 1;
|
||||
parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Register and Wire declarations.
|
||||
//----------------------------------------------------------------
|
||||
reg [31 : 0] cycle_ctr;
|
||||
reg [31 : 0] error_ctr;
|
||||
reg [31 : 0] tc_ctr;
|
||||
reg tb_monitor;
|
||||
|
||||
reg tb_clk;
|
||||
reg tb_reset_n;
|
||||
reg tb_start;
|
||||
reg tb_stop;
|
||||
reg [31 : 0] tb_prescaler;
|
||||
reg [31 : 0] tb_timer;
|
||||
wire [31 : 0] tb_curr_prescaler;
|
||||
wire [31 : 0] tb_curr_timer;
|
||||
wire tb_ready;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Device Under Test.
|
||||
//----------------------------------------------------------------
|
||||
timer_core dut(
|
||||
.clk(tb_clk),
|
||||
.reset_n(tb_reset_n),
|
||||
.prescaler_value(tb_prescaler),
|
||||
.timer_value(tb_timer),
|
||||
.start(tb_start),
|
||||
.stop(tb_stop),
|
||||
.curr_prescaler(tb_curr_prescaler),
|
||||
.curr_timer(tb_curr_timer),
|
||||
.ready(tb_ready)
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// clk_gen
|
||||
//
|
||||
// Always running clock generator process.
|
||||
//----------------------------------------------------------------
|
||||
always
|
||||
begin : clk_gen
|
||||
#CLK_HALF_PERIOD;
|
||||
tb_clk = !tb_clk;
|
||||
end // clk_gen
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// sys_monitor()
|
||||
//
|
||||
// An always running process that creates a cycle counter and
|
||||
// conditionally displays information about the DUT.
|
||||
//----------------------------------------------------------------
|
||||
always
|
||||
begin : sys_monitor
|
||||
cycle_ctr = cycle_ctr + 1;
|
||||
#(CLK_PERIOD);
|
||||
if (tb_monitor)
|
||||
begin
|
||||
dump_dut_state();
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// dump_dut_state()
|
||||
//
|
||||
// Dump the state of the dump when needed.
|
||||
//----------------------------------------------------------------
|
||||
task dump_dut_state;
|
||||
begin
|
||||
$display("State of DUT");
|
||||
$display("------------");
|
||||
$display("Cycle: %08d", cycle_ctr);
|
||||
$display("");
|
||||
$display("Inputs and outputs:");
|
||||
$display("start: 0x%1x, stop: 0x%1x, ready: 0x%1x",
|
||||
dut.start, dut.stop, dut.ready);
|
||||
$display("prescaler_value: 0x%08x, timer_value: 0x%08x",
|
||||
dut.prescaler_value, dut.timer_value);
|
||||
$display("");
|
||||
$display("Internal state:");
|
||||
$display("prescaler_reg: 0x%08x, prescaler_new: 0x%08x",
|
||||
dut.prescaler_reg, dut.prescaler_new);
|
||||
$display("prescaler_set: 0x%1x, prescaler_dec: 0x%1x",
|
||||
dut.prescaler_set, dut.prescaler_dec);
|
||||
$display("");
|
||||
$display("timer_reg: 0x%08x, timer_new: 0x%08x",
|
||||
dut.timer_reg, dut.timer_new);
|
||||
$display("timer_set: 0x%1x, timer_dec: 0x%1x",
|
||||
dut.timer_set, dut.timer_dec);
|
||||
$display("");
|
||||
$display("core_ctrl_reg: 0x%02x, core_ctrl_new: 0x%02x, core_ctrl_we: 0x%1x",
|
||||
dut.core_ctrl_reg, dut.core_ctrl_new, dut.core_ctrl_we);
|
||||
$display("");
|
||||
$display("");
|
||||
end
|
||||
endtask // dump_dut_state
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// reset_dut()
|
||||
//
|
||||
// Toggle reset to put the DUT into a well known state.
|
||||
//----------------------------------------------------------------
|
||||
task reset_dut;
|
||||
begin
|
||||
$display("--- DUT before reset:");
|
||||
dump_dut_state();
|
||||
$display("--- Toggling reset.");
|
||||
tb_reset_n = 0;
|
||||
#(2 * CLK_PERIOD);
|
||||
tb_reset_n = 1;
|
||||
$display("--- DUT after reset:");
|
||||
dump_dut_state();
|
||||
end
|
||||
endtask // reset_dut
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// wait_ready()
|
||||
//
|
||||
// Wait for the ready flag in the dut to be set.
|
||||
//
|
||||
// Note: It is the callers responsibility to call the function
|
||||
// when the dut is actively processing and will in fact at some
|
||||
// point set the flag.
|
||||
//----------------------------------------------------------------
|
||||
task wait_ready;
|
||||
begin
|
||||
#(2 * CLK_PERIOD);
|
||||
while (!tb_ready)
|
||||
begin
|
||||
#(CLK_PERIOD);
|
||||
if (DUMP_WAIT)
|
||||
begin
|
||||
dump_dut_state();
|
||||
end
|
||||
end
|
||||
end
|
||||
endtask // wait_ready
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// init_sim()
|
||||
//
|
||||
// Initialize all counters and testbed functionality as well
|
||||
// as setting the DUT inputs to defined values.
|
||||
//----------------------------------------------------------------
|
||||
task init_sim;
|
||||
begin
|
||||
cycle_ctr = 0;
|
||||
error_ctr = 0;
|
||||
tc_ctr = 0;
|
||||
tb_monitor = 0;
|
||||
|
||||
tb_clk = 0;
|
||||
tb_reset_n = 1;
|
||||
|
||||
tb_start = 1'h0;
|
||||
tb_stop = 1'h0;
|
||||
tb_prescaler = 32'h0;
|
||||
tb_timer = 32'h0;
|
||||
end
|
||||
endtask // init_sim
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// test()
|
||||
// Runs an encipher, decipher test with given key and plaintext
|
||||
// The generated ciphertext is verified with the given ciphertet.
|
||||
// The generated plaintxt is also verified against the
|
||||
// given plaintext.
|
||||
//----------------------------------------------------------------
|
||||
task test1;
|
||||
begin
|
||||
tc_ctr = tc_ctr + 1;
|
||||
tb_monitor = 1;
|
||||
|
||||
$display("--- test1 started.");
|
||||
dump_dut_state();
|
||||
tb_prescaler = 32'h6;
|
||||
tb_timer = 32'h9;
|
||||
#(CLK_PERIOD);
|
||||
tb_start = 1'h1;
|
||||
#(CLK_PERIOD);
|
||||
tb_start = 1'h0;
|
||||
wait_ready();
|
||||
#(CLK_PERIOD);
|
||||
tb_monitor = 0;
|
||||
$display("--- test1 completed.");
|
||||
$display("");
|
||||
end
|
||||
endtask // test1
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// timer_core_test
|
||||
//
|
||||
// Test vectors from:
|
||||
//----------------------------------------------------------------
|
||||
initial
|
||||
begin : timer_core_test
|
||||
$display("--- Simulation of TIMER core started.");
|
||||
$display("");
|
||||
|
||||
init_sim();
|
||||
reset_dut();
|
||||
|
||||
test1();
|
||||
|
||||
$display("");
|
||||
$display("--- Simulation of timer core completed.");
|
||||
$finish;
|
||||
end // timer_core_test
|
||||
endmodule // tb_timer_core
|
||||
|
||||
//======================================================================
|
||||
// EOF tb_timer_core.v
|
||||
//======================================================================
|
75
hw/application_fpga/core/timer/toolruns/Makefile
Executable file
75
hw/application_fpga/core/timer/toolruns/Makefile
Executable file
|
@ -0,0 +1,75 @@
|
|||
#===================================================================
|
||||
#
|
||||
# Makefile
|
||||
# --------
|
||||
# Makefile for building the timer core and top simulations.
|
||||
#
|
||||
#
|
||||
# Author: Joachim Strombergson
|
||||
# Copyright (C) 2022 - Tillitis AB
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
#===================================================================
|
||||
|
||||
CORE_SRC=../rtl/timer_core.v
|
||||
TB_CORE_SRC =../tb/tb_timer_core.v
|
||||
|
||||
TOP_SRC=../rtl/timer.v $(CORE_SRC)
|
||||
TB_TOP_SRC =../tb/tb_timer.v
|
||||
|
||||
CC = iverilog
|
||||
CC_FLAGS = -Wall
|
||||
|
||||
LINT = verilator
|
||||
LINT_FLAGS = +1364-2001ext+ --lint-only -Wall -Wno-fatal -Wno-DECLFILENAME
|
||||
|
||||
|
||||
all: top.sim core.sim
|
||||
|
||||
|
||||
top.sim: $(TB_TOP_SRC) $(TOP_SRC)
|
||||
$(CC) $(CC_FLAGS) -o top.sim $(TB_TOP_SRC) $(TOP_SRC)
|
||||
|
||||
|
||||
core.sim: $(TB_CORE_SRC) $(CORE_SRC)
|
||||
$(CC) $(CC_FLAGS) -o core.sim $(TB_CORE_SRC) $(CORE_SRC)
|
||||
|
||||
|
||||
sim-top: top.sim
|
||||
./top.sim
|
||||
|
||||
|
||||
sim-core: core.sim
|
||||
./core.sim
|
||||
|
||||
|
||||
lint-core: $(CORE_SRC)
|
||||
$(LINT) $(LINT_FLAGS) $(CORE_SRC)
|
||||
|
||||
|
||||
lint-top: $(TOP_SRC)
|
||||
$(LINT) $(LINT_FLAGS) $(TOP_SRC)
|
||||
|
||||
|
||||
clean:
|
||||
rm -f top.sim
|
||||
rm -f core.sim
|
||||
|
||||
|
||||
help:
|
||||
@echo "Build system for simulation of Prince core"
|
||||
@echo ""
|
||||
@echo "Supported targets:"
|
||||
@echo "------------------"
|
||||
@echo "all: Build all simulation targets."
|
||||
@echo "top.sim: Build top level simulation target."
|
||||
@echo "core.sim: Build core level simulation target."
|
||||
@echo "sim-top: Run top level simulation."
|
||||
@echo "sim-core: Run core level simulation."
|
||||
@echo "lint-core: Lint core rtl source files."
|
||||
@echo "lint-core: Lint top rtl source files."
|
||||
@echo "clean: Delete all built files."
|
||||
|
||||
#===================================================================
|
||||
# EOF Makefile
|
||||
#===================================================================
|
5
hw/application_fpga/core/touch_sense/README.md
Normal file
5
hw/application_fpga/core/touch_sense/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# touch_sense
|
||||
Core that handles touch senor events and provides them via an API.
|
||||
|
||||
## Introduction
|
||||
This core implements a touch sensor handler. The core detects and holds events for SW to read. The user must lift the finger between touch events.
|
211
hw/application_fpga/core/touch_sense/rtl/touch_sense.v
Normal file
211
hw/application_fpga/core/touch_sense/rtl/touch_sense.v
Normal file
|
@ -0,0 +1,211 @@
|
|||
//======================================================================
|
||||
//
|
||||
// touch_sense.v
|
||||
// -------------
|
||||
// Touch sensor handler.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module touch_sense(
|
||||
input wire clk,
|
||||
input wire reset_n,
|
||||
|
||||
input wire touch_event,
|
||||
|
||||
input wire cs,
|
||||
input wire we,
|
||||
|
||||
input wire [7 : 0] address,
|
||||
output wire [31 : 0] read_data,
|
||||
output wire ready
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Internal constant and parameter definitions.
|
||||
//----------------------------------------------------------------
|
||||
localparam ADDR_NAME0 = 8'h00;
|
||||
localparam ADDR_NAME1 = 8'h01;
|
||||
localparam ADDR_VERSION = 8'h02;
|
||||
|
||||
localparam ADDR_STATUS = 8'h09;
|
||||
localparam STATUS_EVENT_BIT = 0;
|
||||
|
||||
localparam CORE_NAME0 = 32'h745f7365; // "t_se"
|
||||
localparam CORE_NAME1 = 32'h6e736520; // "nse "
|
||||
localparam CORE_VERSION = 32'h00000001;
|
||||
|
||||
localparam CTRL_IDLE = 2'h0;
|
||||
localparam CTRL_EVENT = 2'h1;
|
||||
localparam CTRL_WAIT = 2'h2;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Registers including update variables and write enable.
|
||||
//----------------------------------------------------------------
|
||||
reg touch_event_sample0_reg;
|
||||
reg touch_event_sample1_reg;
|
||||
|
||||
reg touch_event_reg;
|
||||
reg touch_event_new;
|
||||
reg touch_event_set;
|
||||
reg touch_event_rst;
|
||||
reg touch_event_we;
|
||||
|
||||
reg [1 : 0] touch_sense_ctrl_reg;
|
||||
reg [1 : 0] touch_sense_ctrl_new;
|
||||
reg touch_sense_ctrl_we;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Wires.
|
||||
//----------------------------------------------------------------
|
||||
reg [31 : 0] tmp_read_data;
|
||||
reg [31 : 0] tmp_ready;
|
||||
reg api_clear_event;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Concurrent connectivity for ports etc.
|
||||
//----------------------------------------------------------------
|
||||
assign read_data = tmp_read_data;
|
||||
assign ready = tmp_ready;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// reg_update
|
||||
//----------------------------------------------------------------
|
||||
always @ (posedge clk)
|
||||
begin : reg_update
|
||||
if (!reset_n) begin
|
||||
touch_sense_ctrl_reg <= CTRL_IDLE;
|
||||
touch_event_sample0_reg <= 1'h0;
|
||||
touch_event_sample1_reg <= 1'h0;
|
||||
touch_event_reg <= 1'h0;
|
||||
end
|
||||
|
||||
else begin
|
||||
touch_event_sample0_reg <= touch_event;
|
||||
touch_event_sample1_reg <= touch_event_sample0_reg;
|
||||
|
||||
if (touch_event_we) begin
|
||||
touch_event_reg <= touch_event_new;
|
||||
end
|
||||
|
||||
if (touch_sense_ctrl_we) begin
|
||||
touch_sense_ctrl_reg <= touch_sense_ctrl_new;
|
||||
end
|
||||
end
|
||||
end // reg_update
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// api
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin : api
|
||||
api_clear_event = 1'h0;
|
||||
tmp_read_data = 32'h0;
|
||||
tmp_ready = 1'h0;
|
||||
|
||||
if (cs) begin
|
||||
tmp_ready = 1'h1;
|
||||
|
||||
if (we) begin
|
||||
if (address == ADDR_STATUS) begin
|
||||
api_clear_event = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
else begin
|
||||
if (address == ADDR_NAME0) begin
|
||||
tmp_read_data = CORE_NAME0;
|
||||
end
|
||||
|
||||
if (address == ADDR_NAME1) begin
|
||||
tmp_read_data = CORE_NAME1;
|
||||
end
|
||||
|
||||
if (address == ADDR_VERSION) begin
|
||||
tmp_read_data = CORE_VERSION;
|
||||
end
|
||||
|
||||
if (address == ADDR_STATUS) begin
|
||||
tmp_read_data[STATUS_EVENT_BIT] = touch_event_reg;
|
||||
end
|
||||
end
|
||||
end
|
||||
end // api
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// touch_event_reg_logic
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin : touch_event_reg_logic
|
||||
touch_event_new = 1'h0;
|
||||
touch_event_we = 1'h0;
|
||||
|
||||
if (touch_event_set) begin
|
||||
touch_event_new = 1'h1;
|
||||
touch_event_we = 1'h1;
|
||||
end
|
||||
|
||||
else if (touch_event_rst) begin
|
||||
touch_event_new = 1'h0;
|
||||
touch_event_we = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// touch_sense_ctrl
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin : touch_sense_ctrl
|
||||
touch_event_set = 1'h0;
|
||||
touch_event_rst = 1'h0;
|
||||
touch_sense_ctrl_new = CTRL_IDLE;
|
||||
touch_sense_ctrl_we = 1'h0;
|
||||
|
||||
case (touch_sense_ctrl_reg)
|
||||
CTRL_IDLE : begin
|
||||
if (touch_event_sample1_reg) begin
|
||||
touch_event_set = 1'h1;
|
||||
touch_sense_ctrl_new = CTRL_EVENT;
|
||||
touch_sense_ctrl_we = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
CTRL_EVENT: begin
|
||||
if (api_clear_event) begin
|
||||
touch_event_rst = 1'h1;
|
||||
touch_sense_ctrl_new = CTRL_WAIT;
|
||||
touch_sense_ctrl_we = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
CTRL_WAIT: begin
|
||||
if (!touch_event_sample1_reg) begin
|
||||
touch_sense_ctrl_new = CTRL_IDLE;
|
||||
touch_sense_ctrl_we = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
default : begin
|
||||
end
|
||||
endcase // case (touch_sense_ctrl_reg)
|
||||
end
|
||||
|
||||
endmodule // touch_sense
|
||||
|
||||
//======================================================================
|
||||
// EOF touch_sense.v
|
||||
//======================================================================
|
276
hw/application_fpga/core/touch_sense/tb/tb_touch_sense.v
Normal file
276
hw/application_fpga/core/touch_sense/tb/tb_touch_sense.v
Normal file
|
@ -0,0 +1,276 @@
|
|||
//======================================================================
|
||||
//
|
||||
// tb_touch_sense.v
|
||||
// ----------------
|
||||
// Testbench for the touch sense core.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_touch_sense();
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Internal constant and parameter definitions.
|
||||
//----------------------------------------------------------------
|
||||
parameter DEBUG = 0;
|
||||
parameter DUMP_WAIT = 0;
|
||||
|
||||
parameter CLK_HALF_PERIOD = 1;
|
||||
parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD;
|
||||
|
||||
localparam ADDR_NAME0 = 8'h00;
|
||||
localparam ADDR_NAME1 = 8'h01;
|
||||
localparam ADDR_VERSION = 8'h02;
|
||||
|
||||
localparam ADDR_STATUS = 8'h09;
|
||||
localparam STATUS_READY_BIT = 0;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Register and Wire declarations.
|
||||
//----------------------------------------------------------------
|
||||
reg [31 : 0] cycle_ctr;
|
||||
reg [31 : 0] error_ctr;
|
||||
reg [31 : 0] tc_ctr;
|
||||
reg tb_monitor;
|
||||
|
||||
reg tb_clk;
|
||||
reg tb_reset_n;
|
||||
reg tb_touch_event;
|
||||
reg tb_cs;
|
||||
reg tb_we;
|
||||
reg [7 : 0] tb_address;
|
||||
wire [31 : 0] tb_read_data;
|
||||
|
||||
reg [31 : 0] read_data;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Device Under Test.
|
||||
//----------------------------------------------------------------
|
||||
touch_sense dut(
|
||||
.clk(tb_clk),
|
||||
.reset_n(tb_reset_n),
|
||||
|
||||
.touch_event(tb_touch_event),
|
||||
|
||||
.cs(tb_cs),
|
||||
.we(tb_we),
|
||||
.address(tb_address),
|
||||
.read_data(tb_read_data)
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// clk_gen
|
||||
//
|
||||
// Always running clock generator process.
|
||||
//----------------------------------------------------------------
|
||||
always
|
||||
begin : clk_gen
|
||||
#CLK_HALF_PERIOD;
|
||||
tb_clk = !tb_clk;
|
||||
end // clk_gen
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// sys_monitor()
|
||||
//
|
||||
// An always running process that creates a cycle counter and
|
||||
// conditionally displays information about the DUT.
|
||||
//----------------------------------------------------------------
|
||||
always
|
||||
begin : sys_monitor
|
||||
cycle_ctr = cycle_ctr + 1;
|
||||
#(CLK_PERIOD);
|
||||
if (tb_monitor)
|
||||
begin
|
||||
dump_dut_state();
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// dump_dut_state()
|
||||
//
|
||||
// Dump the state of the dump when needed.
|
||||
//----------------------------------------------------------------
|
||||
task dump_dut_state;
|
||||
begin
|
||||
$display("State of DUT");
|
||||
$display("------------");
|
||||
$display("Cycle: %08d", cycle_ctr);
|
||||
$display("");
|
||||
end
|
||||
endtask // dump_dut_state
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// reset_dut()
|
||||
//
|
||||
// Toggle reset to put the DUT into a well known state.
|
||||
//----------------------------------------------------------------
|
||||
task reset_dut;
|
||||
begin
|
||||
$display("--- Toggle reset.");
|
||||
tb_reset_n = 0;
|
||||
#(2 * CLK_PERIOD);
|
||||
tb_reset_n = 1;
|
||||
end
|
||||
endtask // reset_dut
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// display_test_result()
|
||||
//
|
||||
// Display the accumulated test results.
|
||||
//----------------------------------------------------------------
|
||||
task display_test_result;
|
||||
begin
|
||||
if (error_ctr == 0)
|
||||
begin
|
||||
$display("--- All %02d test cases completed successfully", tc_ctr);
|
||||
end
|
||||
else
|
||||
begin
|
||||
$display("--- %02d tests completed - %02d test cases did not complete successfully.",
|
||||
tc_ctr, error_ctr);
|
||||
end
|
||||
end
|
||||
endtask // display_test_result
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// init_sim()
|
||||
//
|
||||
// Initialize all counters and testbed functionality as well
|
||||
// as setting the DUT inputs to defined values.
|
||||
//----------------------------------------------------------------
|
||||
task init_sim;
|
||||
begin
|
||||
cycle_ctr = 0;
|
||||
error_ctr = 0;
|
||||
tc_ctr = 0;
|
||||
tb_monitor = 0;
|
||||
|
||||
tb_clk = 1'h0;
|
||||
tb_reset_n = 1'h1;
|
||||
tb_touch_event = 1'h0;
|
||||
tb_cs = 1'h0;
|
||||
tb_we = 1'h0;
|
||||
tb_address = 8'h0;
|
||||
end
|
||||
endtask // init_sim
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// write_word()
|
||||
//
|
||||
// Write the given word to the DUT using the DUT interface.
|
||||
//----------------------------------------------------------------
|
||||
task write_word(input [11 : 0] address,
|
||||
input [31 : 0] word);
|
||||
begin
|
||||
if (DEBUG)
|
||||
begin
|
||||
$display("--- Writing 0x%08x to 0x%02x.", word, address);
|
||||
$display("");
|
||||
end
|
||||
|
||||
tb_address = address;
|
||||
tb_cs = 1;
|
||||
tb_we = 1;
|
||||
#(2 * CLK_PERIOD);
|
||||
tb_cs = 0;
|
||||
tb_we = 0;
|
||||
end
|
||||
endtask // write_word
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// read_word()
|
||||
//
|
||||
// Read a data word from the given address in the DUT.
|
||||
// the word read will be available in the global variable
|
||||
// read_data.
|
||||
//----------------------------------------------------------------
|
||||
task read_word(input [11 : 0] address);
|
||||
begin
|
||||
tb_address = address;
|
||||
tb_cs = 1;
|
||||
tb_we = 0;
|
||||
#(CLK_PERIOD);
|
||||
read_data = tb_read_data;
|
||||
tb_cs = 0;
|
||||
|
||||
if (DEBUG)
|
||||
begin
|
||||
$display("--- Reading 0x%08x from 0x%02x.", read_data, address);
|
||||
$display("");
|
||||
end
|
||||
end
|
||||
endtask // read_word
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// wait_ready()
|
||||
//
|
||||
// Wait for the ready flag to be set in dut.
|
||||
//----------------------------------------------------------------
|
||||
task wait_ready;
|
||||
begin : wready
|
||||
read_word(ADDR_STATUS);
|
||||
while (read_data == 0)
|
||||
read_word(ADDR_STATUS);
|
||||
end
|
||||
endtask // wait_ready
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// test1()
|
||||
//----------------------------------------------------------------
|
||||
task test1;
|
||||
begin
|
||||
tc_ctr = tc_ctr + 1;
|
||||
|
||||
$display("");
|
||||
$display("--- test1: started.");
|
||||
|
||||
$display("--- test1: completed.");
|
||||
$display("");
|
||||
end
|
||||
endtask // tes1
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// touch_sense_test
|
||||
//----------------------------------------------------------------
|
||||
initial
|
||||
begin : timer_test
|
||||
$display("");
|
||||
$display(" -= Testbench for touch_sense started =-");
|
||||
$display(" ====================================");
|
||||
$display("");
|
||||
|
||||
init_sim();
|
||||
reset_dut();
|
||||
test1();
|
||||
|
||||
display_test_result();
|
||||
$display("");
|
||||
$display(" -= Testbench for touch_sense completed =-");
|
||||
$display(" ======================================");
|
||||
$display("");
|
||||
$finish;
|
||||
end // touch_sense_test
|
||||
endmodule // tb_touch_sense
|
||||
|
||||
//======================================================================
|
||||
// EOF tb_touch_sense.v
|
||||
//======================================================================
|
56
hw/application_fpga/core/touch_sense/toolruns/Makefile
Executable file
56
hw/application_fpga/core/touch_sense/toolruns/Makefile
Executable file
|
@ -0,0 +1,56 @@
|
|||
#===================================================================
|
||||
#
|
||||
# Makefile
|
||||
# --------
|
||||
# Makefile for building the touch sense top simulations.
|
||||
#
|
||||
#
|
||||
# Author: Joachim Strombergson
|
||||
# Copyright (C) 2022 - Tillitis AB
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
#===================================================================
|
||||
|
||||
TOP_SRC=../rtl/touch_sense.v
|
||||
TB_TOP_SRC =../tb/tb_touch_sense.v
|
||||
|
||||
CC = iverilog
|
||||
CC_FLAGS = -Wall
|
||||
|
||||
LINT = verilator
|
||||
LINT_FLAGS = +1364-2001ext+ --lint-only -Wall -Wno-fatal -Wno-DECLFILENAME
|
||||
|
||||
|
||||
all: top.sim
|
||||
|
||||
|
||||
top.sim: $(TB_TOP_SRC) $(TOP_SRC)
|
||||
$(CC) $(CC_FLAGS) -o top.sim $(TB_TOP_SRC) $(TOP_SRC)
|
||||
|
||||
|
||||
sim-top: top.sim
|
||||
./top.sim
|
||||
|
||||
|
||||
lint-top: $(TOP_SRC)
|
||||
$(LINT) $(LINT_FLAGS) $(TOP_SRC)
|
||||
|
||||
|
||||
clean:
|
||||
rm -f top.sim
|
||||
|
||||
|
||||
help:
|
||||
@echo "Build system for simulation of touch sense core"
|
||||
@echo ""
|
||||
@echo "Supported targets:"
|
||||
@echo "------------------"
|
||||
@echo "all: Build all simulation targets."
|
||||
@echo "top.sim: Build top level simulation target."
|
||||
@echo "sim-top: Run top level simulation."
|
||||
@echo "lint-top: Lint top rtl source files."
|
||||
@echo "clean: Delete all built files."
|
||||
|
||||
#===================================================================
|
||||
# EOF Makefile
|
||||
#===================================================================
|
29
hw/application_fpga/core/trng/README.md
Normal file
29
hw/application_fpga/core/trng/README.md
Normal file
|
@ -0,0 +1,29 @@
|
|||
# trng
|
||||
Implementation of the FiGaRO TRNG for FPGAs
|
||||
|
||||
## Introduction
|
||||
# figaro
|
||||
|
||||
|
||||
## Status
|
||||
First version completed. In testing. Use with caution.
|
||||
|
||||
|
||||
## Introduction
|
||||
This is a an implementation of the FiGaRO true random
|
||||
number generator (TRNG) [1]. The main FPGA target is Lattice iCE40
|
||||
UltraPlus, but adaption to other FPGAs should be easy to do.
|
||||
|
||||
|
||||
## Implementation details
|
||||
The implementation instantiates four FiRO and four GaRO modules. The
|
||||
modules includes state sampling. The polynomials used for the
|
||||
oscillators are given by equotions (9)..(16) in paper [1]. The eight
|
||||
outputs are then XORed together to form a one bit random value.
|
||||
|
||||
The random bit value is sampled at a rate controlled by a 24 bit
|
||||
divisor.
|
||||
|
||||
## References
|
||||
[1] [True Random Number Generator Based on Fibonacci-Galois
|
||||
Ring Oscillators for FPGA](https://www.mdpi.com/2076-3417/11/8/3330/pdf)
|
129
hw/application_fpga/core/trng/rtl/figaro.v
Normal file
129
hw/application_fpga/core/trng/rtl/figaro.v
Normal file
|
@ -0,0 +1,129 @@
|
|||
//======================================================================
|
||||
//
|
||||
// figaro.v
|
||||
// --------
|
||||
// Top level wrapper for the figaro core.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module figaro(
|
||||
input wire clk,
|
||||
input wire reset_n,
|
||||
|
||||
input wire cs,
|
||||
input wire we,
|
||||
input wire [7 : 0] address,
|
||||
input wire [31 : 0] write_data,
|
||||
output wire [31 : 0] read_data,
|
||||
output wire ready
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Internal constant and parameter definitions.
|
||||
//----------------------------------------------------------------
|
||||
localparam ADDR_NAME0 = 8'h00;
|
||||
localparam ADDR_NAME1 = 8'h01;
|
||||
localparam ADDR_VERSION = 8'h02;
|
||||
|
||||
localparam ADDR_STATUS = 8'h09;
|
||||
localparam STATUS_READY_BIT = 0;
|
||||
|
||||
localparam ADDR_SAMPLE_RATE = 8'h10;
|
||||
|
||||
localparam ADDR_ENTROPY = 8'h20;
|
||||
|
||||
localparam CORE_NAME0 = 32'h66696761; // "figa"
|
||||
localparam CORE_NAME1 = 32'h726f2020; // "ro "
|
||||
localparam CORE_VERSION = 32'h00000001;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Wires.
|
||||
//----------------------------------------------------------------
|
||||
reg core_read_entropy;
|
||||
reg core_set_sample_rate;
|
||||
wire [31 : 0] core_entropy;
|
||||
wire core_ready;
|
||||
reg [31 : 0] tmp_read_data;
|
||||
reg tmp_ready;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Concurrent connectivity for ports etc.
|
||||
//----------------------------------------------------------------
|
||||
assign read_data = tmp_read_data;
|
||||
assign ready = tmp_ready;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// core instantiation.
|
||||
//----------------------------------------------------------------
|
||||
figaro_core core(
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
.read_entropy(core_read_entropy),
|
||||
.set_sample_rate(core_set_sample_rate),
|
||||
.sample_rate(write_data[23 : 0]),
|
||||
.entropy(core_entropy),
|
||||
.ready(core_ready)
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// api
|
||||
//
|
||||
// The interface command decoding logic.
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin : api
|
||||
core_read_entropy = 1'h0;
|
||||
core_set_sample_rate = 1'h0;
|
||||
tmp_read_data = 32'h0;
|
||||
tmp_ready = 1'h0;
|
||||
|
||||
if (cs) begin
|
||||
tmp_ready = 1'h1;
|
||||
|
||||
if (we) begin
|
||||
if (address == ADDR_SAMPLE_RATE) begin
|
||||
core_set_sample_rate = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
else begin
|
||||
if (address == ADDR_NAME0) begin
|
||||
tmp_read_data = CORE_NAME0;
|
||||
end
|
||||
|
||||
if (address == ADDR_NAME1) begin
|
||||
tmp_read_data = CORE_NAME1;
|
||||
end
|
||||
|
||||
if (address == ADDR_VERSION) begin
|
||||
tmp_read_data = CORE_VERSION;
|
||||
end
|
||||
|
||||
if (address == ADDR_STATUS) begin
|
||||
tmp_read_data = {31'h0, core_ready};
|
||||
end
|
||||
|
||||
if (address == ADDR_ENTROPY) begin
|
||||
tmp_read_data = core_entropy;
|
||||
core_read_entropy = 1'h1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end // api
|
||||
endmodule // figaro
|
||||
|
||||
//======================================================================
|
||||
// EOF figaro.v
|
||||
//======================================================================
|
204
hw/application_fpga/core/trng/rtl/figaro_core.v
Normal file
204
hw/application_fpga/core/trng/rtl/figaro_core.v
Normal file
|
@ -0,0 +1,204 @@
|
|||
//======================================================================
|
||||
//
|
||||
// figaro_core.v
|
||||
// -----------
|
||||
// FiGaRO based FIGARO for iCE40 device.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
module figaro_core(
|
||||
input wire clk,
|
||||
input wire reset_n,
|
||||
|
||||
input wire set_sample_rate,
|
||||
input wire [23 : 0] sample_rate,
|
||||
|
||||
input wire read_entropy,
|
||||
output wire [31 : 0] entropy,
|
||||
|
||||
output wire ready
|
||||
);
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Local parameters.
|
||||
//---------------------------------------------------------------
|
||||
localparam DEFAULT_SAMPLE_RATE = 24'h010000;
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Registers.
|
||||
//---------------------------------------------------------------
|
||||
reg [23 : 0] sample_rate_ctr_reg;
|
||||
reg [23 : 0] sample_rate_ctr_new;
|
||||
|
||||
reg [23 : 0] sample_rate_reg;
|
||||
reg sample_rate_we;
|
||||
|
||||
reg [5 : 0] bit_ctr_reg;
|
||||
reg [5 : 0] bit_ctr_new;
|
||||
reg bit_ctr_rst;
|
||||
reg bit_ctr_inc;
|
||||
reg bit_ctr_we;
|
||||
|
||||
reg [31 : 0] entropy_reg;
|
||||
reg [31 : 0] entropy_new;
|
||||
reg entropy_we;
|
||||
|
||||
reg ready_reg;
|
||||
reg ready_new;
|
||||
reg ready_we;
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Firo oscillator instances and XOR combined result.
|
||||
//---------------------------------------------------------------
|
||||
wire firo_ent[3 : 0];
|
||||
wire firo_entropy;
|
||||
|
||||
firo #(.POLY(10'b1111110111)) firo0(.clk(clk), .entropy(firo_ent[0]));
|
||||
firo #(.POLY(10'b1011111001)) firo1(.clk(clk), .entropy(firo_ent[1]));
|
||||
firo #(.POLY(10'b1100000001)) firo2(.clk(clk), .entropy(firo_ent[2]));
|
||||
firo #(.POLY(10'b1011111111)) firo3(.clk(clk), .entropy(firo_ent[3]));
|
||||
|
||||
assign firo_entropy = firo_ent[0] ^ firo_ent[1] ^
|
||||
firo_ent[2] ^ firo_ent[3];
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// garo oscillator instances and XOR combined result.
|
||||
//---------------------------------------------------------------
|
||||
wire garo_ent[3 : 0];
|
||||
wire garo_entropy;
|
||||
|
||||
garo #(.POLY(11'b11111101111)) garo0(.clk(clk), .entropy(garo_ent[0]));
|
||||
garo #(.POLY(11'b10111110011)) garo1(.clk(clk), .entropy(garo_ent[1]));
|
||||
garo #(.POLY(11'b11000000011)) garo2(.clk(clk), .entropy(garo_ent[2]));
|
||||
garo #(.POLY(11'b10111111111)) garo3(.clk(clk), .entropy(garo_ent[3]));
|
||||
|
||||
assign garo_entropy = garo_ent[0] ^ garo_ent[1] ^
|
||||
garo_ent[2] ^ garo_ent[3];
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Assignments.
|
||||
//---------------------------------------------------------------
|
||||
assign ready = ready_reg;
|
||||
assign entropy = entropy_reg;
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// reg_update
|
||||
//---------------------------------------------------------------
|
||||
always @(posedge clk)
|
||||
begin : reg_update
|
||||
if (!reset_n) begin
|
||||
sample_rate_reg <= DEFAULT_SAMPLE_RATE;
|
||||
sample_rate_ctr_reg <= 24'h0;
|
||||
bit_ctr_reg <= 6'h0;
|
||||
entropy_reg <= 32'h0;
|
||||
ready_reg <= 1'h0;
|
||||
end
|
||||
else begin
|
||||
sample_rate_ctr_reg <= sample_rate_ctr_new;
|
||||
|
||||
if (sample_rate_we) begin
|
||||
sample_rate_reg <= sample_rate;
|
||||
end
|
||||
|
||||
if (bit_ctr_we) begin
|
||||
bit_ctr_reg <= bit_ctr_new;
|
||||
end
|
||||
|
||||
if (entropy_we) begin
|
||||
entropy_reg <= entropy_new;
|
||||
end
|
||||
|
||||
if (ready_we) begin
|
||||
ready_reg <= ready_new;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// ready_logic
|
||||
//
|
||||
// After an entropy word has been read we wait 32 bits before
|
||||
// setting ready again, indicating that a new word is ready.
|
||||
//---------------------------------------------------------------
|
||||
always @*
|
||||
begin : ready_logic;
|
||||
bit_ctr_new = 6'h0;
|
||||
bit_ctr_we = 1'h0;
|
||||
ready_new = 1'h0;
|
||||
ready_we = 1'h0;
|
||||
|
||||
if (bit_ctr_reg >= 6'h20) begin
|
||||
ready_new = 1'h1;
|
||||
ready_we = 1'h1;
|
||||
end
|
||||
|
||||
if (bit_ctr_rst) begin
|
||||
bit_ctr_new = 6'h0;
|
||||
bit_ctr_we = 1'h1;
|
||||
ready_new = 1'h0;
|
||||
ready_we = 1'h1;
|
||||
end
|
||||
|
||||
else if (bit_ctr_inc) begin
|
||||
if (bit_ctr_reg < 6'h24) begin
|
||||
bit_ctr_new = bit_ctr_reg + 1'h1;
|
||||
bit_ctr_we = 1'h1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// figaro_sample_logic
|
||||
//
|
||||
// Wait sample_rate_reg number of cycles between sampling a bit
|
||||
// from the entropy source.
|
||||
//---------------------------------------------------------------
|
||||
always @*
|
||||
begin : figaro_sample_logic
|
||||
sample_rate_we = 1'h0;
|
||||
bit_ctr_rst = 1'h0;
|
||||
bit_ctr_inc = 1'h0;
|
||||
entropy_we = 1'h0;
|
||||
|
||||
entropy_new = {entropy_reg[30 : 0], firo_entropy ^ garo_entropy};
|
||||
|
||||
if (read_entropy) begin
|
||||
bit_ctr_rst = 1'h1;
|
||||
sample_rate_ctr_new = 24'h0;
|
||||
end
|
||||
|
||||
else if (set_sample_rate) begin
|
||||
bit_ctr_rst = 1'h1;
|
||||
sample_rate_we = 1'h1;
|
||||
sample_rate_ctr_new = 24'h0;
|
||||
end
|
||||
|
||||
else if (sample_rate_ctr_reg == sample_rate_reg) begin
|
||||
sample_rate_ctr_new = 24'h0;
|
||||
entropy_we = 1'h1;
|
||||
bit_ctr_inc = 1'h1;
|
||||
end
|
||||
|
||||
else begin
|
||||
sample_rate_ctr_new = sample_rate_ctr_reg + 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule // figaro_core
|
||||
|
||||
//======================================================================
|
||||
// EOF figaro_core.v
|
||||
//======================================================================
|
75
hw/application_fpga/core/trng/rtl/firo.v
Normal file
75
hw/application_fpga/core/trng/rtl/firo.v
Normal file
|
@ -0,0 +1,75 @@
|
|||
//======================================================================
|
||||
//
|
||||
// firo.v
|
||||
// ------
|
||||
// Fibonacci Ring Oscillator with state sampling.
|
||||
// The Fibonacci depth is 10 bits, and the bits are always sampled.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module firo(
|
||||
input wire clk,
|
||||
output wire entropy
|
||||
);
|
||||
|
||||
parameter POLY = 10'b1111111111;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Registers and wires.
|
||||
//----------------------------------------------------------------
|
||||
reg entropy_reg;
|
||||
wire [10 : 0] f;
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Combinational loop inverters.
|
||||
//---------------------------------------------------------------
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv1 (.I0(f[0]), .O(f[1]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv2 (.I0(f[1]), .O(f[2]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv3 (.I0(f[2]), .O(f[3]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv4 (.I0(f[3]), .O(f[4]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv5 (.I0(f[4]), .O(f[5]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv6 (.I0(f[5]), .O(f[6]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv7 (.I0(f[6]), .O(f[7]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv8 (.I0(f[7]), .O(f[8]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv9 (.I0(f[8]), .O(f[9]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv10 (.I0(f[9]), .O(f[10]));
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// parameterized feedback logic.
|
||||
//---------------------------------------------------------------
|
||||
assign f[0] = (POLY[0] & f[1]) ^ (POLY[1] & f[2]) ^
|
||||
(POLY[2] & f[3]) ^ (POLY[3] & f[4]) ^
|
||||
(POLY[4] & f[5]) ^ (POLY[5] & f[6]) ^
|
||||
(POLY[6] & f[7]) ^ (POLY[7] & f[8]) ^
|
||||
(POLY[8] & f[9]) ^ (POLY[9] & f[10]);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Concurrent connectivity for ports etc.
|
||||
//----------------------------------------------------------------
|
||||
assign entropy = entropy_reg;
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// reg_update
|
||||
//---------------------------------------------------------------
|
||||
always @(posedge clk)
|
||||
begin : reg_update
|
||||
entropy_reg <= ^f;
|
||||
end
|
||||
|
||||
endmodule // firo
|
||||
|
||||
//======================================================================
|
||||
// EOF firo.v
|
||||
//======================================================================
|
85
hw/application_fpga/core/trng/rtl/garo.v
Normal file
85
hw/application_fpga/core/trng/rtl/garo.v
Normal file
|
@ -0,0 +1,85 @@
|
|||
//======================================================================
|
||||
//
|
||||
// garo.v
|
||||
// ------
|
||||
// GaloisRing Oscillator with state sampling.
|
||||
// The Galois depth is 11 bits, and the bits are always sampled.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module garo(
|
||||
input wire clk,
|
||||
output wire entropy
|
||||
);
|
||||
|
||||
parameter POLY = 11'b11111111111;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Registers and wires.
|
||||
//----------------------------------------------------------------
|
||||
reg entropy_reg;
|
||||
wire [11 : 0] g;
|
||||
wire [11 : 0] gp;
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// Combinational loop inverters.
|
||||
//---------------------------------------------------------------
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv1 (.I0(g[0]), .O(gp[0]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv2 (.I0(g[1]), .O(gp[1]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv3 (.I0(g[2]), .O(gp[2]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv4 (.I0(g[3]), .O(gp[3]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv5 (.I0(g[4]), .O(gp[4]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv6 (.I0(g[5]), .O(gp[5]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv7 (.I0(g[6]), .O(gp[6]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv8 (.I0(g[7]), .O(gp[7]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv9 (.I0(g[8]), .O(gp[8]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv10 (.I0(g[9]), .O(gp[9]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv11 (.I0(g[10]), .O(gp[10]));
|
||||
(* keep *) SB_LUT4 #(.LUT_INIT(1'b1)) osc_inv12 (.I0(g[11]), .O(gp[11]));
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// parameterized feedback logic.
|
||||
//---------------------------------------------------------------
|
||||
assign g[11] = gp[0];
|
||||
assign g[10] = gp[11] ^ (POLY[10] & gp[0]);
|
||||
assign g[9] = gp[10] ^ (POLY[9] & gp[0]);
|
||||
assign g[8] = gp[9] ^ (POLY[8] & gp[0]);
|
||||
assign g[7] = gp[8] ^ (POLY[7] & gp[0]);
|
||||
assign g[6] = gp[7] ^ (POLY[6] & gp[0]);
|
||||
assign g[5] = gp[6] ^ (POLY[5] & gp[0]);
|
||||
assign g[4] = gp[5] ^ (POLY[4] & gp[0]);
|
||||
assign g[3] = gp[4] ^ (POLY[3] & gp[0]);
|
||||
assign g[2] = gp[3] ^ (POLY[2] & gp[0]);
|
||||
assign g[1] = gp[2] ^ (POLY[1] & gp[0]);
|
||||
assign g[0] = gp[1] ^ (POLY[0] & gp[0]);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Concurrent connectivity for ports etc.
|
||||
//----------------------------------------------------------------
|
||||
assign entropy = entropy_reg;
|
||||
|
||||
|
||||
//---------------------------------------------------------------
|
||||
// reg_update
|
||||
//---------------------------------------------------------------
|
||||
always @(posedge clk)
|
||||
begin : reg_update
|
||||
entropy_reg <= ^g;
|
||||
end
|
||||
|
||||
endmodule // garo
|
||||
|
||||
//======================================================================
|
||||
// EOF garo.v
|
||||
//======================================================================
|
23
hw/application_fpga/core/uart/LICENSE
Normal file
23
hw/application_fpga/core/uart/LICENSE
Normal file
|
@ -0,0 +1,23 @@
|
|||
Copyright (c) 2014, Joachim Strömbergson
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
9
hw/application_fpga/core/uart/README.md
Normal file
9
hw/application_fpga/core/uart/README.md
Normal file
|
@ -0,0 +1,9 @@
|
|||
uart
|
||||
====
|
||||
|
||||
A simple universal asynchronous receiver/transmitter (UART) core
|
||||
implemented in Verilog.
|
||||
|
||||
|
||||
# Status
|
||||
The core is completed and has been used in several FPGA designs.
|
306
hw/application_fpga/core/uart/rtl/uart.v
Normal file
306
hw/application_fpga/core/uart/rtl/uart.v
Normal file
|
@ -0,0 +1,306 @@
|
|||
//======================================================================
|
||||
//
|
||||
// uart.v
|
||||
// ------
|
||||
// Top level wrapper for the uart core.
|
||||
//
|
||||
// A simple universal asynchronous receiver/transmitter (UART)
|
||||
// interface. The interface contains 16 byte wide transmit and
|
||||
// receivea buffers and can handle start and stop bits. But in
|
||||
// general is rather simple. The primary purpose is as host
|
||||
// interface for the coretest design. The core also has a
|
||||
// loopback mode to allow testing of a serial link.
|
||||
//
|
||||
// Note that the UART has a separate API interface to allow
|
||||
// a control core to change settings such as speed. But the core
|
||||
// has default values to allow it to start operating directly
|
||||
// after reset. No config should be needed.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (c) 2014, Secworks Sweden AB
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or
|
||||
// without modification, are permitted provided that the following
|
||||
// conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
module uart(
|
||||
input wire clk,
|
||||
input wire reset_n,
|
||||
|
||||
input wire rxd,
|
||||
output wire txd,
|
||||
|
||||
input wire cs,
|
||||
input wire we,
|
||||
input wire [7 : 0] address,
|
||||
input wire [31 : 0] write_data,
|
||||
output wire [31 : 0] read_data,
|
||||
output wire ready
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Internal constant and parameter definitions.
|
||||
//----------------------------------------------------------------
|
||||
localparam ADDR_CORE_NAME0 = 8'h00;
|
||||
localparam ADDR_CORE_NAME1 = 8'h01;
|
||||
localparam ADDR_CORE_VERSION = 8'h02;
|
||||
|
||||
localparam ADDR_BIT_RATE = 8'h10;
|
||||
localparam ADDR_DATA_BITS = 8'h11;
|
||||
localparam ADDR_STOP_BITS = 8'h12;
|
||||
|
||||
localparam ADDR_RX_STATUS = 8'h20;
|
||||
localparam ADDR_RX_DATA = 8'h21;
|
||||
|
||||
localparam ADDR_TX_STATUS = 8'h40;
|
||||
localparam ADDR_TX_DATA = 8'h41;
|
||||
|
||||
|
||||
localparam CORE_NAME0 = 32'h75617274; // "uart"
|
||||
localparam CORE_NAME1 = 32'h20202020; // " "
|
||||
localparam CORE_VERSION = 32'h00000004;
|
||||
|
||||
|
||||
// The default bit rate is based on target clock frequency
|
||||
// divided by the bit rate times in order to hit the
|
||||
// center of the bits. I.e.
|
||||
// Clock: 12 MHz, 38400 bps
|
||||
// Divisor = 12*10E6 / 38400 = 312
|
||||
localparam DEFAULT_BIT_RATE = 16'd312;
|
||||
localparam DEFAULT_DATA_BITS = 4'h8;
|
||||
localparam DEFAULT_STOP_BITS = 2'h1;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Registers including update variables and write enable.
|
||||
//----------------------------------------------------------------
|
||||
reg [15 : 0] bit_rate_reg;
|
||||
reg bit_rate_we;
|
||||
|
||||
reg [3 : 0] data_bits_reg;
|
||||
reg data_bits_we;
|
||||
|
||||
reg [1 : 0] stop_bits_reg;
|
||||
reg stop_bits_we;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Wires.
|
||||
//----------------------------------------------------------------
|
||||
wire core_rxd_syn;
|
||||
wire [7 : 0] core_rxd_data;
|
||||
wire core_rxd_ack;
|
||||
|
||||
reg core_txd_syn;
|
||||
reg [7 : 0] core_txd_data;
|
||||
wire core_txd_ready;
|
||||
|
||||
wire fifo_out_syn;
|
||||
wire [7 : 0] fifo_out_data;
|
||||
reg fifo_out_ack;
|
||||
|
||||
reg [31 : 0] tmp_read_data;
|
||||
reg tmp_ready;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Concurrent connectivity for ports etc.
|
||||
//----------------------------------------------------------------
|
||||
assign read_data = tmp_read_data;
|
||||
assign ready = tmp_ready;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Module instantiations.
|
||||
//----------------------------------------------------------------
|
||||
uart_core core(
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
// Configuration parameters
|
||||
.bit_rate(bit_rate_reg),
|
||||
.data_bits(data_bits_reg),
|
||||
.stop_bits(stop_bits_reg),
|
||||
|
||||
// External data interface
|
||||
.rxd(rxd),
|
||||
.txd(txd),
|
||||
|
||||
// Internal receive interface.
|
||||
.rxd_syn(core_rxd_syn),
|
||||
.rxd_data(core_rxd_data),
|
||||
.rxd_ack(core_rxd_ack),
|
||||
|
||||
// Internal transmit interface.
|
||||
.txd_syn(core_txd_syn),
|
||||
.txd_data(core_txd_data),
|
||||
.txd_ready(core_txd_ready)
|
||||
);
|
||||
|
||||
|
||||
uart_fifo fifo(
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.in_syn(core_rxd_syn),
|
||||
.in_data(core_rxd_data),
|
||||
.in_ack(core_rxd_ack),
|
||||
|
||||
.out_syn(fifo_out_syn),
|
||||
.out_data(fifo_out_data),
|
||||
.out_ack(fifo_out_ack)
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// reg_update
|
||||
//
|
||||
// Update functionality for all registers in the core.
|
||||
// All registers are positive edge triggered with synchronous
|
||||
// active low reset.
|
||||
//----------------------------------------------------------------
|
||||
always @ (posedge clk)
|
||||
begin: reg_update
|
||||
if (!reset_n) begin
|
||||
bit_rate_reg <= DEFAULT_BIT_RATE;
|
||||
data_bits_reg <= DEFAULT_DATA_BITS;
|
||||
stop_bits_reg <= DEFAULT_STOP_BITS;
|
||||
end
|
||||
else begin
|
||||
if (bit_rate_we) begin
|
||||
bit_rate_reg <= write_data[15 : 0];
|
||||
end
|
||||
|
||||
if (data_bits_we) begin
|
||||
data_bits_reg <= write_data[3 : 0];
|
||||
end
|
||||
|
||||
if (stop_bits_we) begin
|
||||
stop_bits_reg <= write_data[1 : 0];
|
||||
end
|
||||
end
|
||||
end // reg_update
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// api
|
||||
//
|
||||
// The core API that allows an internal host to control the
|
||||
// core functionality.
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin: api
|
||||
// Default assignments.
|
||||
bit_rate_we = 1'h0;
|
||||
data_bits_we = 1'h0;
|
||||
stop_bits_we = 1'h0;
|
||||
core_txd_syn = 1'h0;
|
||||
fifo_out_ack = 1'h0;
|
||||
tmp_read_data = 32'h0;
|
||||
tmp_ready = 1'h0;
|
||||
|
||||
core_txd_data = write_data[7 : 0];
|
||||
|
||||
if (cs) begin
|
||||
tmp_ready = 1'h1;
|
||||
|
||||
if (we) begin
|
||||
case (address)
|
||||
ADDR_BIT_RATE: begin
|
||||
bit_rate_we = 1;
|
||||
end
|
||||
|
||||
ADDR_DATA_BITS: begin
|
||||
data_bits_we = 1;
|
||||
end
|
||||
|
||||
ADDR_STOP_BITS: begin
|
||||
stop_bits_we = 1;
|
||||
end
|
||||
|
||||
ADDR_TX_DATA: begin
|
||||
core_txd_syn = 1'h1;
|
||||
end
|
||||
|
||||
default: begin
|
||||
end
|
||||
endcase // case (address)
|
||||
end
|
||||
|
||||
else begin
|
||||
case (address)
|
||||
ADDR_CORE_NAME0: begin
|
||||
tmp_read_data = CORE_NAME0;
|
||||
end
|
||||
|
||||
ADDR_CORE_NAME1: begin
|
||||
tmp_read_data = CORE_NAME1;
|
||||
end
|
||||
|
||||
ADDR_CORE_VERSION: begin
|
||||
tmp_read_data = CORE_VERSION;
|
||||
end
|
||||
|
||||
ADDR_BIT_RATE: begin
|
||||
tmp_read_data = {16'h0, bit_rate_reg};
|
||||
end
|
||||
|
||||
ADDR_DATA_BITS: begin
|
||||
tmp_read_data = {28'h0, data_bits_reg};
|
||||
end
|
||||
|
||||
ADDR_STOP_BITS: begin
|
||||
tmp_read_data = {30'h0, stop_bits_reg};
|
||||
end
|
||||
|
||||
ADDR_RX_STATUS: begin
|
||||
tmp_read_data = {31'h0, fifo_out_syn};
|
||||
end
|
||||
|
||||
ADDR_RX_DATA: begin
|
||||
fifo_out_ack = 1'h1;
|
||||
tmp_read_data = {24'h0, fifo_out_data};
|
||||
end
|
||||
|
||||
ADDR_TX_STATUS: begin
|
||||
tmp_read_data = {31'h0, core_txd_ready};
|
||||
end
|
||||
|
||||
default: begin
|
||||
end
|
||||
endcase // case (address)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule // uart
|
||||
|
||||
//======================================================================
|
||||
// EOF uart.v
|
||||
//======================================================================
|
532
hw/application_fpga/core/uart/rtl/uart_core.v
Normal file
532
hw/application_fpga/core/uart/rtl/uart_core.v
Normal file
|
@ -0,0 +1,532 @@
|
|||
//======================================================================
|
||||
//
|
||||
// uart_core.v
|
||||
// -----------
|
||||
// A simple universal asynchronous receiver/transmitter (UART)
|
||||
// interface. The interface contains 16 byte wide transmit and
|
||||
// receivea buffers and can handle start and stop bits. But in
|
||||
// general is rather simple. The primary purpose is as host
|
||||
// interface for the coretest design. The core also has a
|
||||
// loopback mode to allow testing of a serial link.
|
||||
//
|
||||
// Note that the UART has a separate API interface to allow
|
||||
// a control core to change settings such as speed. But the core
|
||||
// has default values to allow it to start operating directly
|
||||
// after reset. No config should be needed.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (c) 2014, Secworks Sweden AB
|
||||
//
|
||||
// SPDX-License-Identifier: BSD-2-Clause
|
||||
// Redistribution and use in source and binary forms, with or
|
||||
// without modification, are permitted provided that the following
|
||||
// conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
module uart_core(
|
||||
input wire clk,
|
||||
input wire reset_n,
|
||||
|
||||
// Configuration parameters
|
||||
input wire [15 : 0] bit_rate,
|
||||
input wire [3 : 0] data_bits,
|
||||
input wire [1 : 0] stop_bits,
|
||||
|
||||
// External data interface
|
||||
input wire rxd,
|
||||
output wire txd,
|
||||
|
||||
// Internal receive interface.
|
||||
output wire rxd_syn,
|
||||
output [7 : 0] rxd_data,
|
||||
input wire rxd_ack,
|
||||
|
||||
// Internal transmit interface.
|
||||
input wire txd_syn,
|
||||
input wire [7 : 0] txd_data,
|
||||
output wire txd_ready
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Internal constant and parameter definitions.
|
||||
//----------------------------------------------------------------
|
||||
parameter ERX_IDLE = 0;
|
||||
parameter ERX_START = 1;
|
||||
parameter ERX_BITS = 2;
|
||||
parameter ERX_STOP = 3;
|
||||
parameter ERX_SYN = 4;
|
||||
|
||||
parameter ETX_IDLE = 0;
|
||||
parameter ETX_ACK = 1;
|
||||
parameter ETX_START = 2;
|
||||
parameter ETX_BITS = 3;
|
||||
parameter ETX_STOP = 4;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Registers including update variables and write enable.
|
||||
//----------------------------------------------------------------
|
||||
reg rxd0_reg;
|
||||
reg rxd_reg;
|
||||
|
||||
reg [7 : 0] rxd_byte_reg;
|
||||
reg rxd_byte_we;
|
||||
|
||||
reg [4 : 0] rxd_bit_ctr_reg;
|
||||
reg [4 : 0] rxd_bit_ctr_new;
|
||||
reg rxd_bit_ctr_we;
|
||||
reg rxd_bit_ctr_rst;
|
||||
reg rxd_bit_ctr_inc;
|
||||
|
||||
reg [15 : 0] rxd_bitrate_ctr_reg;
|
||||
reg [15 : 0] rxd_bitrate_ctr_new;
|
||||
reg rxd_bitrate_ctr_we;
|
||||
reg rxd_bitrate_ctr_rst;
|
||||
reg rxd_bitrate_ctr_inc;
|
||||
|
||||
reg rxd_syn_reg;
|
||||
reg rxd_syn_new;
|
||||
reg rxd_syn_we;
|
||||
|
||||
reg [2 : 0] erx_ctrl_reg;
|
||||
reg [2 : 0] erx_ctrl_new;
|
||||
reg erx_ctrl_we;
|
||||
|
||||
reg txd_reg;
|
||||
reg txd_new;
|
||||
reg txd_we;
|
||||
|
||||
reg [7 : 0] txd_byte_reg;
|
||||
reg [7 : 0] txd_byte_new;
|
||||
reg txd_byte_we;
|
||||
|
||||
reg [4 : 0] txd_bit_ctr_reg;
|
||||
reg [4 : 0] txd_bit_ctr_new;
|
||||
reg txd_bit_ctr_we;
|
||||
reg txd_bit_ctr_rst;
|
||||
reg txd_bit_ctr_inc;
|
||||
|
||||
reg [15 : 0] txd_bitrate_ctr_reg;
|
||||
reg [15 : 0] txd_bitrate_ctr_new;
|
||||
reg txd_bitrate_ctr_we;
|
||||
reg txd_bitrate_ctr_rst;
|
||||
reg txd_bitrate_ctr_inc;
|
||||
|
||||
reg txd_ready_reg;
|
||||
reg txd_ready_new;
|
||||
reg txd_ready_we;
|
||||
|
||||
reg [2 : 0] etx_ctrl_reg;
|
||||
reg [2 : 0] etx_ctrl_new;
|
||||
reg etx_ctrl_we;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Wires.
|
||||
//----------------------------------------------------------------
|
||||
wire [15 : 0] half_bit_rate;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Concurrent connectivity for ports etc.
|
||||
//----------------------------------------------------------------
|
||||
assign txd = txd_reg;
|
||||
assign rxd_syn = rxd_syn_reg;
|
||||
assign rxd_data = rxd_byte_reg;
|
||||
assign txd_ready = txd_ready_reg;
|
||||
|
||||
assign half_bit_rate = {1'b0, bit_rate[15 : 1]};
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// reg_update
|
||||
//
|
||||
// Update functionality for all registers in the core.
|
||||
// All registers are positive edge triggered with
|
||||
// synchronous active low reset.
|
||||
//----------------------------------------------------------------
|
||||
always @ (posedge clk)
|
||||
begin: reg_update
|
||||
if (!reset_n) begin
|
||||
rxd0_reg <= 1'b0;
|
||||
rxd_reg <= 1'b0;
|
||||
rxd_byte_reg <= 8'h0;
|
||||
rxd_bit_ctr_reg <= 5'h0;
|
||||
rxd_bitrate_ctr_reg <= 16'h0;
|
||||
rxd_syn_reg <= 0;
|
||||
erx_ctrl_reg <= ERX_IDLE;
|
||||
|
||||
txd_reg <= 1'b1;
|
||||
txd_byte_reg <= 8'h0;
|
||||
txd_bit_ctr_reg <= 5'h0;
|
||||
txd_bitrate_ctr_reg <= 16'h0;
|
||||
txd_ready_reg <= 1'b1;
|
||||
etx_ctrl_reg <= ETX_IDLE;
|
||||
end
|
||||
|
||||
else begin
|
||||
rxd0_reg <= rxd;
|
||||
rxd_reg <= rxd0_reg;
|
||||
|
||||
if (rxd_byte_we) begin
|
||||
rxd_byte_reg <= {rxd_reg, rxd_byte_reg[7 : 1]};
|
||||
end
|
||||
|
||||
if (rxd_bit_ctr_we) begin
|
||||
rxd_bit_ctr_reg <= rxd_bit_ctr_new;
|
||||
end
|
||||
|
||||
if (rxd_bitrate_ctr_we) begin
|
||||
rxd_bitrate_ctr_reg <= rxd_bitrate_ctr_new;
|
||||
end
|
||||
|
||||
if (rxd_syn_we) begin
|
||||
rxd_syn_reg <= rxd_syn_new;
|
||||
end
|
||||
|
||||
if (erx_ctrl_we) begin
|
||||
erx_ctrl_reg <= erx_ctrl_new;
|
||||
end
|
||||
|
||||
if (txd_we) begin
|
||||
txd_reg <= txd_new;
|
||||
end
|
||||
|
||||
if (txd_byte_we) begin
|
||||
txd_byte_reg <= txd_byte_new;
|
||||
end
|
||||
|
||||
if (txd_bit_ctr_we) begin
|
||||
txd_bit_ctr_reg <= txd_bit_ctr_new;
|
||||
end
|
||||
|
||||
if (txd_bitrate_ctr_we) begin
|
||||
txd_bitrate_ctr_reg <= txd_bitrate_ctr_new;
|
||||
end
|
||||
|
||||
if (txd_ready_we) begin
|
||||
txd_ready_reg <= txd_ready_new;
|
||||
end
|
||||
|
||||
if (etx_ctrl_we) begin
|
||||
etx_ctrl_reg <= etx_ctrl_new;
|
||||
end
|
||||
end
|
||||
end // reg_update
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// rxd_bit_ctr
|
||||
//
|
||||
// Bit counter for receiving data on the external
|
||||
// serial interface.
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin: rxd_bit_ctr
|
||||
rxd_bit_ctr_new = 5'h0;
|
||||
rxd_bit_ctr_we = 1'b0;
|
||||
|
||||
if (rxd_bit_ctr_rst) begin
|
||||
rxd_bit_ctr_new = 5'h0;
|
||||
rxd_bit_ctr_we = 1'b1;
|
||||
end
|
||||
|
||||
else if (rxd_bit_ctr_inc) begin
|
||||
rxd_bit_ctr_new = rxd_bit_ctr_reg + 1'h1;
|
||||
rxd_bit_ctr_we = 1'b1;
|
||||
end
|
||||
end // rxd_bit_ctr
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// rxd_bitrate_ctr
|
||||
//
|
||||
// Bitrate counter for receiving data on the external
|
||||
// serial interface.
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin: rxd_bitrate_ctr
|
||||
rxd_bitrate_ctr_new = 16'h0;
|
||||
rxd_bitrate_ctr_we = 1'h0;
|
||||
|
||||
if (rxd_bitrate_ctr_rst) begin
|
||||
rxd_bitrate_ctr_new = 16'h0;
|
||||
rxd_bitrate_ctr_we = 1'b1;
|
||||
end
|
||||
|
||||
else if (rxd_bitrate_ctr_inc) begin
|
||||
rxd_bitrate_ctr_new = rxd_bitrate_ctr_reg + 1'h1;
|
||||
rxd_bitrate_ctr_we = 1'b1;
|
||||
end
|
||||
end // rxd_bitrate_ctr
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// txd_bit_ctr
|
||||
//
|
||||
// Bit counter for transmitting data on the external
|
||||
// serial interface.
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin: txd_bit_ctr
|
||||
txd_bit_ctr_new = 5'h0;
|
||||
txd_bit_ctr_we = 1'h0;
|
||||
|
||||
if (txd_bit_ctr_rst) begin
|
||||
txd_bit_ctr_new = 5'h0;
|
||||
txd_bit_ctr_we = 1'h1;
|
||||
end
|
||||
|
||||
else if (txd_bit_ctr_inc) begin
|
||||
txd_bit_ctr_new = txd_bit_ctr_reg + 1'h1;
|
||||
txd_bit_ctr_we = 1'b1;
|
||||
end
|
||||
end // txd_bit_ctr
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// txd_bitrate_ctr
|
||||
//
|
||||
// Bitrate counter for transmitting data on the external
|
||||
// serial interface.
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin: txd_bitrate_ctr
|
||||
txd_bitrate_ctr_new = 16'h0;
|
||||
txd_bitrate_ctr_we = 0;
|
||||
|
||||
if (txd_bitrate_ctr_rst) begin
|
||||
txd_bitrate_ctr_new = 16'h0;
|
||||
txd_bitrate_ctr_we = 1;
|
||||
end
|
||||
|
||||
else if (txd_bitrate_ctr_inc) begin
|
||||
txd_bitrate_ctr_new = txd_bitrate_ctr_reg + 1'h1;
|
||||
txd_bitrate_ctr_we = 1;
|
||||
end
|
||||
end // txd_bitrate_ctr
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// external_rx_engine
|
||||
//
|
||||
// Logic that implements the receive engine towards
|
||||
// the external interface. Detects incoming data, collects it,
|
||||
// if required checks parity and store correct data into
|
||||
// the rx buffer.
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin: external_rx_engine
|
||||
rxd_bit_ctr_rst = 0;
|
||||
rxd_bit_ctr_inc = 0;
|
||||
rxd_bitrate_ctr_rst = 0;
|
||||
rxd_bitrate_ctr_inc = 0;
|
||||
rxd_byte_we = 0;
|
||||
rxd_syn_new = 0;
|
||||
rxd_syn_we = 0;
|
||||
erx_ctrl_new = ERX_IDLE;
|
||||
erx_ctrl_we = 0;
|
||||
|
||||
case (erx_ctrl_reg)
|
||||
ERX_IDLE: begin
|
||||
if (!rxd_reg) begin
|
||||
// Possible start bit detected.
|
||||
rxd_bitrate_ctr_rst = 1;
|
||||
erx_ctrl_new = ERX_START;
|
||||
erx_ctrl_we = 1;
|
||||
end
|
||||
end
|
||||
|
||||
ERX_START: begin
|
||||
rxd_bitrate_ctr_inc = 1;
|
||||
if (rxd_reg) begin
|
||||
// Just a glitch.
|
||||
erx_ctrl_new = ERX_IDLE;
|
||||
erx_ctrl_we = 1;
|
||||
end
|
||||
|
||||
else begin
|
||||
if (rxd_bitrate_ctr_reg == half_bit_rate) begin
|
||||
// start bit assumed. We start sampling data.
|
||||
rxd_bit_ctr_rst = 1;
|
||||
rxd_bitrate_ctr_rst = 1;
|
||||
erx_ctrl_new = ERX_BITS;
|
||||
erx_ctrl_we = 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
ERX_BITS: begin
|
||||
if (rxd_bitrate_ctr_reg < bit_rate) begin
|
||||
rxd_bitrate_ctr_inc = 1;
|
||||
end
|
||||
|
||||
else begin
|
||||
rxd_byte_we = 1;
|
||||
rxd_bit_ctr_inc = 1;
|
||||
rxd_bitrate_ctr_rst = 1;
|
||||
if (rxd_bit_ctr_reg == data_bits - 1) begin
|
||||
erx_ctrl_new = ERX_STOP;
|
||||
erx_ctrl_we = 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
ERX_STOP: begin
|
||||
rxd_bitrate_ctr_inc = 1;
|
||||
if (rxd_bitrate_ctr_reg == bit_rate * stop_bits) begin
|
||||
rxd_syn_new = 1;
|
||||
rxd_syn_we = 1;
|
||||
erx_ctrl_new = ERX_SYN;
|
||||
erx_ctrl_we = 1;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
ERX_SYN: begin
|
||||
if (rxd_ack) begin
|
||||
rxd_syn_new = 0;
|
||||
rxd_syn_we = 1;
|
||||
erx_ctrl_new = ERX_IDLE;
|
||||
erx_ctrl_we = 1;
|
||||
end
|
||||
end
|
||||
|
||||
default: begin
|
||||
end
|
||||
|
||||
endcase // case (erx_ctrl_reg)
|
||||
end // external_rx_engine
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// external_tx_engine
|
||||
//
|
||||
// Logic that implements the transmit engine towards
|
||||
// the external interface.
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin: external_tx_engine
|
||||
txd_new = 0;
|
||||
txd_we = 0;
|
||||
txd_byte_new = 0;
|
||||
txd_byte_we = 0;
|
||||
txd_bit_ctr_rst = 0;
|
||||
txd_bit_ctr_inc = 0;
|
||||
txd_bitrate_ctr_rst = 0;
|
||||
txd_bitrate_ctr_inc = 0;
|
||||
txd_ready_new = 0;
|
||||
txd_ready_we = 0;
|
||||
etx_ctrl_new = ETX_IDLE;
|
||||
etx_ctrl_we = 0;
|
||||
|
||||
case (etx_ctrl_reg)
|
||||
ETX_IDLE: begin
|
||||
txd_new = 1;
|
||||
txd_we = 1;
|
||||
if (txd_syn) begin
|
||||
txd_byte_new = txd_data;
|
||||
txd_byte_we = 1;
|
||||
txd_ready_new = 0;
|
||||
txd_ready_we = 1;
|
||||
txd_bitrate_ctr_rst = 1;
|
||||
etx_ctrl_new = ETX_ACK;
|
||||
etx_ctrl_we = 1;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
ETX_ACK: begin
|
||||
if (!txd_syn) begin
|
||||
txd_new = 0;
|
||||
txd_we = 1;
|
||||
etx_ctrl_new = ETX_START;
|
||||
etx_ctrl_we = 1;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
ETX_START: begin
|
||||
if (txd_bitrate_ctr_reg == bit_rate) begin
|
||||
txd_bit_ctr_rst = 1;
|
||||
etx_ctrl_new = ETX_BITS;
|
||||
etx_ctrl_we = 1;
|
||||
end
|
||||
|
||||
else begin
|
||||
txd_bitrate_ctr_inc = 1;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
ETX_BITS: begin
|
||||
if (txd_bitrate_ctr_reg < bit_rate) begin
|
||||
txd_bitrate_ctr_inc = 1;
|
||||
end
|
||||
|
||||
else begin
|
||||
txd_bitrate_ctr_rst = 1;
|
||||
if (txd_bit_ctr_reg == data_bits) begin
|
||||
txd_new = 1;
|
||||
txd_we = 1;
|
||||
etx_ctrl_new = ETX_STOP;
|
||||
etx_ctrl_we = 1;
|
||||
end
|
||||
|
||||
else begin
|
||||
txd_new = txd_byte_reg[txd_bit_ctr_reg];
|
||||
txd_we = 1;
|
||||
txd_bit_ctr_inc = 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
ETX_STOP: begin
|
||||
txd_bitrate_ctr_inc = 1;
|
||||
if (txd_bitrate_ctr_reg == bit_rate * stop_bits) begin
|
||||
txd_ready_new = 1;
|
||||
txd_ready_we = 1;
|
||||
etx_ctrl_new = ETX_IDLE;
|
||||
etx_ctrl_we = 1;
|
||||
end
|
||||
end
|
||||
|
||||
default: begin
|
||||
end
|
||||
|
||||
endcase // case (etx_ctrl_reg)
|
||||
end // external_tx_engine
|
||||
|
||||
endmodule // uart
|
||||
|
||||
//======================================================================
|
||||
// EOF uart.v
|
||||
//======================================================================
|
179
hw/application_fpga/core/uart/rtl/uart_fifo.v
Normal file
179
hw/application_fpga/core/uart/rtl/uart_fifo.v
Normal file
|
@ -0,0 +1,179 @@
|
|||
//======================================================================
|
||||
//
|
||||
// uart_fifo.v
|
||||
// -----------
|
||||
// FIFO for rx and tx data buffering in the UART.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (c) 2022, Tillitis AB
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or
|
||||
// without modification, are permitted provided that the following
|
||||
// conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
module uart_fifo(
|
||||
input wire clk,
|
||||
input wire reset_n,
|
||||
|
||||
input wire in_syn,
|
||||
input wire [7 : 0] in_data,
|
||||
output wire in_ack,
|
||||
|
||||
output wire out_syn,
|
||||
output wire [7 : 0] out_data,
|
||||
input wire out_ack
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Registers including update variables and write enable.
|
||||
//----------------------------------------------------------------
|
||||
reg [7 : 0] fifo_mem [0 : 255];
|
||||
reg fifo_mem_we;
|
||||
|
||||
reg [7: 0] in_ptr_reg;
|
||||
reg [7: 0] in_ptr_new;
|
||||
reg in_ptr_inc;
|
||||
reg in_ptr_we;
|
||||
|
||||
reg [7: 0] out_ptr_reg;
|
||||
reg [7: 0] out_ptr_new;
|
||||
reg out_ptr_inc;
|
||||
reg out_ptr_we;
|
||||
|
||||
reg [7: 0] byte_ctr_reg;
|
||||
reg [7: 0] byte_ctr_new;
|
||||
reg byte_ctr_inc;
|
||||
reg byte_ctr_dec;
|
||||
reg byte_ctr_we;
|
||||
|
||||
reg in_ack_reg;
|
||||
reg in_ack_new;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Concurrent connectivity for ports etc.
|
||||
//----------------------------------------------------------------
|
||||
assign in_ack = in_ack_reg;
|
||||
|
||||
assign out_syn = |byte_ctr_reg;
|
||||
assign out_data = fifo_mem[out_ptr_reg];
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// reg_update
|
||||
//----------------------------------------------------------------
|
||||
always @ (posedge clk)
|
||||
begin: reg_update
|
||||
if (!reset_n) begin
|
||||
in_ptr_reg <= 8'h0;
|
||||
out_ptr_reg <= 8'h0;
|
||||
byte_ctr_reg <= 8'h0;
|
||||
in_ack_reg <= 1'h0;
|
||||
end
|
||||
else begin
|
||||
in_ack_reg <= in_ack_new;
|
||||
|
||||
if (fifo_mem_we) begin
|
||||
fifo_mem[in_ptr_reg] <= in_data;
|
||||
end
|
||||
|
||||
if (in_ptr_we) begin
|
||||
in_ptr_reg <= in_ptr_new;
|
||||
end
|
||||
|
||||
if (out_ptr_we) begin
|
||||
out_ptr_reg <= out_ptr_new;
|
||||
end
|
||||
|
||||
if (byte_ctr_we) begin
|
||||
byte_ctr_reg <= byte_ctr_new;
|
||||
end
|
||||
end
|
||||
end // reg_update
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// byte_ctr
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin : byte_ctr
|
||||
byte_ctr_new = 8'h0;
|
||||
byte_ctr_we = 1'h0;
|
||||
|
||||
if ((byte_ctr_inc) && (!byte_ctr_dec)) begin
|
||||
byte_ctr_new = byte_ctr_reg + 1'h1;
|
||||
byte_ctr_we = 1'h1;
|
||||
end
|
||||
|
||||
else if ((!byte_ctr_inc) && (byte_ctr_dec)) begin
|
||||
byte_ctr_new = byte_ctr_reg - 1'h1;
|
||||
byte_ctr_we = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// in_logic
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin : in_logic
|
||||
fifo_mem_we = 1'h0;
|
||||
in_ack_new = 1'h0;
|
||||
byte_ctr_inc = 1'h0;
|
||||
in_ptr_new = in_ptr_reg + 1'h1;
|
||||
in_ptr_we = 1'h0;
|
||||
|
||||
if ((in_syn) && (!in_ack) && (byte_ctr_reg < 8'hff)) begin
|
||||
fifo_mem_we = 1'h1;
|
||||
in_ack_new = 1'h1;
|
||||
byte_ctr_inc = 1'h1;
|
||||
in_ptr_we = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// out_logic
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin : out_logic
|
||||
byte_ctr_dec = 1'h0;
|
||||
out_ptr_new = out_ptr_reg + 1'h1;
|
||||
out_ptr_we = 1'h0;
|
||||
|
||||
if ((out_ack) && (byte_ctr_reg > 8'h0)) begin
|
||||
byte_ctr_dec = 1'h1;
|
||||
out_ptr_we = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule // uart_fifo
|
||||
|
||||
//======================================================================
|
||||
// EOF uart_fifo.v
|
||||
//======================================================================
|
389
hw/application_fpga/core/uart/tb/tb_uart.v
Normal file
389
hw/application_fpga/core/uart/tb/tb_uart.v
Normal file
|
@ -0,0 +1,389 @@
|
|||
//======================================================================
|
||||
//
|
||||
// tb_uart.v
|
||||
// ---------
|
||||
// Testbench for the UART core.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (c) 2014, Secworks Sweden AB
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or
|
||||
// without modification, are permitted provided that the following
|
||||
// conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in
|
||||
// the documentation and/or other materials provided with the
|
||||
// distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
||||
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
module tb_uart();
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Internal constant and parameter definitions.
|
||||
//----------------------------------------------------------------
|
||||
parameter DEBUG = 0;
|
||||
parameter VERBOSE = 0;
|
||||
|
||||
parameter CLK_HALF_PERIOD = 1;
|
||||
parameter CLK_PERIOD = CLK_HALF_PERIOD * 2;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Register and Wire declarations.
|
||||
//----------------------------------------------------------------
|
||||
reg [31 : 0] cycle_ctr;
|
||||
reg [31 : 0] error_ctr;
|
||||
reg [31 : 0] tc_ctr;
|
||||
|
||||
reg tb_clk;
|
||||
reg tb_reset_n;
|
||||
reg tb_rxd;
|
||||
wire tb_txd;
|
||||
wire tb_rxd_syn;
|
||||
wire [7 : 0] tb_rxd_data;
|
||||
wire tb_rxd_ack;
|
||||
wire tb_txd_syn;
|
||||
wire [7 : 0] tb_txd_data;
|
||||
wire tb_txd_ack;
|
||||
reg tb_cs;
|
||||
reg tb_we;
|
||||
reg [7 : 0] tb_address;
|
||||
reg [31 : 0] tb_write_data;
|
||||
wire [31 : 0] tb_read_data;
|
||||
wire tb_error;
|
||||
wire [7 : 0] tb_debug;
|
||||
|
||||
reg txd_state;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Device Under Test.
|
||||
//----------------------------------------------------------------
|
||||
uart dut(
|
||||
.clk(tb_clk),
|
||||
.reset_n(tb_reset_n),
|
||||
|
||||
.rxd(tb_rxd),
|
||||
.txd(tb_txd),
|
||||
|
||||
.rxd_syn(tb_rxd_syn),
|
||||
.rxd_data(tb_rxd_data),
|
||||
.rxd_ack(tb_rxd_ack),
|
||||
|
||||
// Internal transmit interface.
|
||||
.txd_syn(tb_txd_syn),
|
||||
.txd_data(tb_txd_data),
|
||||
.txd_ack(tb_txd_ack),
|
||||
|
||||
// API interface.
|
||||
.cs(tb_cs),
|
||||
.we(tb_we),
|
||||
.address(tb_address),
|
||||
.write_data(tb_write_data),
|
||||
.read_data(tb_read_data),
|
||||
.error(tb_error),
|
||||
|
||||
.debug(tb_debug)
|
||||
);
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Concurrent assignments.
|
||||
//----------------------------------------------------------------
|
||||
// We connect the internal facing ports on the dut together.
|
||||
assign tb_txd_syn = tb_rxd_syn;
|
||||
assign tb_txd_data = tb_rxd_data;
|
||||
assign tb_rxd_ack = tb_txd_ack;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// clk_gen
|
||||
//
|
||||
// Clock generator process.
|
||||
//----------------------------------------------------------------
|
||||
always
|
||||
begin : clk_gen
|
||||
#CLK_HALF_PERIOD tb_clk = !tb_clk;
|
||||
end // clk_gen
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// sys_monitor
|
||||
//----------------------------------------------------------------
|
||||
always
|
||||
begin : sys_monitor
|
||||
#(CLK_PERIOD);
|
||||
if (DEBUG)
|
||||
begin
|
||||
dump_rx_state();
|
||||
dump_tx_state();
|
||||
$display("");
|
||||
end
|
||||
if (VERBOSE)
|
||||
begin
|
||||
$display("cycle: 0x%016x", cycle_ctr);
|
||||
end
|
||||
cycle_ctr = cycle_ctr + 1;
|
||||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// tx_monitor
|
||||
//
|
||||
// Observes what happens on the dut tx port and reports it.
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin : tx_monitor
|
||||
if ((!tb_txd) && txd_state)
|
||||
begin
|
||||
$display("txd going low.");
|
||||
txd_state = 0;
|
||||
end
|
||||
|
||||
if (tb_txd && (!txd_state))
|
||||
begin
|
||||
$display("txd going high");
|
||||
txd_state = 1;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// dump_dut_state()
|
||||
//
|
||||
// Dump the state of the dut when needed.
|
||||
//----------------------------------------------------------------
|
||||
task dump_dut_state;
|
||||
begin
|
||||
$display("State of DUT");
|
||||
$display("------------");
|
||||
$display("Inputs and outputs:");
|
||||
$display("rxd = 0x%01x, txd = 0x%01x,",
|
||||
dut.core.rxd, dut.core.txd);
|
||||
$display("");
|
||||
|
||||
$display("Sample and data registers:");
|
||||
$display("rxd_reg = 0x%01x, rxd_byte_reg = 0x%01x",
|
||||
dut.core.rxd_reg, dut.core.rxd_byte_reg);
|
||||
$display("");
|
||||
|
||||
$display("Counters:");
|
||||
$display("rxd_bit_ctr_reg = 0x%01x, rxd_bitrate_ctr_reg = 0x%02x",
|
||||
dut.core.rxd_bit_ctr_reg, dut.core.rxd_bitrate_ctr_reg);
|
||||
$display("");
|
||||
|
||||
|
||||
$display("Control signals and FSM state:");
|
||||
$display("erx_ctrl_reg = 0x%02x",
|
||||
dut.core.erx_ctrl_reg);
|
||||
$display("");
|
||||
end
|
||||
endtask // dump_dut_state
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// dump_rx_state()
|
||||
//
|
||||
// Dump the state of the rx engine.
|
||||
//----------------------------------------------------------------
|
||||
task dump_rx_state;
|
||||
begin
|
||||
$display("rxd = 0x%01x, rxd_reg = 0x%01x, rxd_byte_reg = 0x%01x, rxd_bit_ctr_reg = 0x%01x, rxd_bitrate_ctr_reg = 0x%02x, rxd_syn = 0x%01x, erx_ctrl_reg = 0x%02x",
|
||||
dut.core.rxd, dut.core.rxd_reg, dut.core.rxd_byte_reg, dut.core.rxd_bit_ctr_reg,
|
||||
dut.core.rxd_bitrate_ctr_reg, dut.core.rxd_syn, dut.core.erx_ctrl_reg);
|
||||
end
|
||||
endtask // dump_dut_state
|
||||
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// dump_tx_state()
|
||||
//
|
||||
// Dump the state of the tx engine.
|
||||
//----------------------------------------------------------------
|
||||
task dump_tx_state;
|
||||
begin
|
||||
$display("txd = 0x%01x, txd_reg = 0x%01x, txd_byte_reg = 0x%01x, txd_bit_ctr_reg = 0x%01x, txd_bitrate_ctr_reg = 0x%02x, txd_ack = 0x%01x, etx_ctrl_reg = 0x%02x",
|
||||
dut.core.txd, dut.core.txd_reg, dut.core.txd_byte_reg, dut.core.txd_bit_ctr_reg,
|
||||
dut.core.txd_bitrate_ctr_reg, dut.core.txd_ack, dut.core.etx_ctrl_reg);
|
||||
end
|
||||
endtask // dump_dut_state
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// reset_dut()
|
||||
//----------------------------------------------------------------
|
||||
task reset_dut;
|
||||
begin
|
||||
$display("*** Toggle reset.");
|
||||
tb_reset_n = 0;
|
||||
#(2 * CLK_PERIOD);
|
||||
tb_reset_n = 1;
|
||||
end
|
||||
endtask // reset_dut
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// init_sim()
|
||||
//
|
||||
// Initialize all counters and testbed functionality as well
|
||||
// as setting the DUT inputs to defined values.
|
||||
//----------------------------------------------------------------
|
||||
task init_sim;
|
||||
begin
|
||||
cycle_ctr = 0;
|
||||
error_ctr = 0;
|
||||
tc_ctr = 0;
|
||||
|
||||
tb_clk = 0;
|
||||
tb_reset_n = 1;
|
||||
tb_rxd = 1;
|
||||
tb_cs = 0;
|
||||
tb_we = 0;
|
||||
tb_address = 8'h00;
|
||||
tb_write_data = 32'h00000000;
|
||||
|
||||
txd_state = 1;
|
||||
end
|
||||
endtask // init_sim
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// transmit_byte
|
||||
//
|
||||
// Transmit a byte of data to the DUT receive port.
|
||||
//----------------------------------------------------------------
|
||||
task transmit_byte(input [7 : 0] data);
|
||||
integer i;
|
||||
begin
|
||||
$display("*** Transmitting byte 0x%02x to the dut.", data);
|
||||
|
||||
#10;
|
||||
|
||||
// Start bit
|
||||
$display("*** Transmitting start bit.");
|
||||
tb_rxd = 0;
|
||||
#(CLK_PERIOD * dut.DEFAULT_BIT_RATE);
|
||||
|
||||
// Send the bits LSB first.
|
||||
for (i = 0 ; i < 8 ; i = i + 1)
|
||||
begin
|
||||
$display("*** Transmitting data[%1d] = 0x%01x.", i, data[i]);
|
||||
tb_rxd = data[i];
|
||||
#(CLK_PERIOD * dut.DEFAULT_BIT_RATE);
|
||||
end
|
||||
|
||||
// Send two stop bits. I.e. two bit times high (mark) value.
|
||||
$display("*** Transmitting two stop bits.");
|
||||
tb_rxd = 1;
|
||||
#(2 * CLK_PERIOD * dut.DEFAULT_BIT_RATE * dut.DEFAULT_STOP_BITS);
|
||||
$display("*** End of transmission.");
|
||||
end
|
||||
endtask // transmit_byte
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// check_transmit
|
||||
//
|
||||
// Transmits a byte and checks that it was captured internally
|
||||
// by the dut.
|
||||
//----------------------------------------------------------------
|
||||
task check_transmit(input [7 : 0] data);
|
||||
begin
|
||||
tc_ctr = tc_ctr + 1;
|
||||
|
||||
transmit_byte(data);
|
||||
|
||||
if (dut.core.rxd_byte_reg == data)
|
||||
begin
|
||||
$display("*** Correct data: 0x%01x captured by the dut.",
|
||||
dut.core.rxd_byte_reg);
|
||||
end
|
||||
else
|
||||
begin
|
||||
$display("*** Incorrect data: 0x%01x captured by the dut Should be: 0x%01x.",
|
||||
dut.core.rxd_byte_reg, data);
|
||||
error_ctr = error_ctr + 1;
|
||||
end
|
||||
end
|
||||
endtask // check_transmit
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// test_transmit
|
||||
//
|
||||
// Transmit a number of test bytes to the dut.
|
||||
//----------------------------------------------------------------
|
||||
task test_transmit;
|
||||
begin
|
||||
check_transmit(8'h55);
|
||||
check_transmit(8'h42);
|
||||
check_transmit(8'hde);
|
||||
check_transmit(8'had);
|
||||
end
|
||||
endtask // test_transmit
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// display_test_result()
|
||||
//
|
||||
// Display the accumulated test results.
|
||||
//----------------------------------------------------------------
|
||||
task display_test_result;
|
||||
begin
|
||||
if (error_ctr == 0)
|
||||
begin
|
||||
$display("*** All %02d test cases completed successfully", tc_ctr);
|
||||
end
|
||||
else
|
||||
begin
|
||||
$display("*** %02d test cases did not complete successfully.", error_ctr);
|
||||
end
|
||||
end
|
||||
endtask // display_test_result
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// uart_test
|
||||
// The main test functionality.
|
||||
//----------------------------------------------------------------
|
||||
initial
|
||||
begin : uart_test
|
||||
$display(" -- Testbench for uart core started --");
|
||||
|
||||
init_sim();
|
||||
dump_dut_state();
|
||||
reset_dut();
|
||||
dump_dut_state();
|
||||
|
||||
test_transmit();
|
||||
|
||||
display_test_result();
|
||||
$display("*** Simulation done.");
|
||||
$finish;
|
||||
end // uart_test
|
||||
endmodule // tb_uart
|
||||
|
||||
//======================================================================
|
||||
// EOF tb_uart.v
|
||||
//======================================================================
|
7
hw/application_fpga/core/uds/README.txt
Normal file
7
hw/application_fpga/core/uds/README.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
# uds
|
||||
|
||||
Unique Device Secret core
|
||||
|
||||
## Introduction
|
||||
This core store and protect the Unique Device Secret. The
|
||||
storage is implemented in discrete registers. The contents can be read once between chip reset, and only if the system is in not in application access mode.
|
129
hw/application_fpga/core/uds/rtl/uds.v
Normal file
129
hw/application_fpga/core/uds/rtl/uds.v
Normal file
|
@ -0,0 +1,129 @@
|
|||
//======================================================================
|
||||
//
|
||||
// uds.v
|
||||
// --------
|
||||
// Top level wrapper for the uds core.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module uds(
|
||||
input wire clk,
|
||||
input wire reset_n,
|
||||
|
||||
output wire fw_app_mode,
|
||||
|
||||
input wire cs,
|
||||
input wire [7 : 0] address,
|
||||
output wire [31 : 0] read_data,
|
||||
output wire ready
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Internal constant and parameter definitions.
|
||||
//----------------------------------------------------------------
|
||||
localparam ADDR_NAME0 = 8'h00;
|
||||
localparam ADDR_NAME1 = 8'h01;
|
||||
localparam ADDR_VERSION = 8'h02;
|
||||
|
||||
localparam ADDR_UDS_FIRST = 8'h10;
|
||||
localparam ADDR_UDS_LAST = 8'h17;
|
||||
|
||||
localparam CORE_NAME0 = 32'h7564735f; // "uds_"
|
||||
localparam CORE_NAME1 = 32'h6d656d20; // "mem "
|
||||
localparam CORE_VERSION = 32'h00000001;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Registers including update variables and write enable.
|
||||
//----------------------------------------------------------------
|
||||
reg [31 : 0] uds_reg [0 : 7];
|
||||
initial $readmemh(`UDS_HEX, uds_reg);
|
||||
|
||||
reg uds_rd_reg [0 : 7];
|
||||
reg uds_rd_we;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Wires.
|
||||
//----------------------------------------------------------------
|
||||
reg [31 : 0] tmp_read_data;
|
||||
reg tmp_ready;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Concurrent connectivity for ports etc.
|
||||
//----------------------------------------------------------------
|
||||
assign read_data = tmp_read_data;
|
||||
assign ready = tmp_ready;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// reg_update
|
||||
//----------------------------------------------------------------
|
||||
always @ (posedge clk)
|
||||
begin : reg_update
|
||||
integer i;
|
||||
|
||||
if (!reset_n) begin
|
||||
for (i = 0 ; i < 8 ; i = i + 1) begin
|
||||
uds_rd_reg[i] <= 1'h0;;
|
||||
end
|
||||
end
|
||||
else begin
|
||||
if (uds_rd_we) begin
|
||||
uds_rd_reg[address[2 : 0]] <= 1'h1;
|
||||
end
|
||||
end
|
||||
end // reg_update
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// api
|
||||
//
|
||||
// The interface command decoding logic.
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin : api
|
||||
uds_rd_we = 1'h0;
|
||||
tmp_read_data = 32'h0;
|
||||
tmp_ready = 1'h0;
|
||||
|
||||
if (cs) begin
|
||||
tmp_ready = 1'h1;
|
||||
|
||||
if (address == ADDR_NAME0) begin
|
||||
tmp_read_data = CORE_NAME0;
|
||||
end
|
||||
|
||||
if (address == ADDR_NAME1) begin
|
||||
tmp_read_data = CORE_NAME1;
|
||||
end
|
||||
|
||||
if (address == ADDR_VERSION) begin
|
||||
tmp_read_data = CORE_VERSION;
|
||||
end
|
||||
|
||||
if ((address >= ADDR_UDS_FIRST) && (address <= ADDR_UDS_LAST)) begin
|
||||
if (!fw_app_mode) begin
|
||||
if (uds_rd_reg[address[2 : 0]] == 1'h0) begin
|
||||
tmp_read_data = uds_reg[address[2 : 0]];
|
||||
uds_rd_we = 1'h1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
endmodule // uds
|
||||
|
||||
//======================================================================
|
||||
// EOF uds.v
|
||||
//======================================================================
|
300
hw/application_fpga/core/uds/tb/tb_uds.v
Normal file
300
hw/application_fpga/core/uds/tb/tb_uds.v
Normal file
|
@ -0,0 +1,300 @@
|
|||
//======================================================================
|
||||
//
|
||||
// tb_uds.v
|
||||
// -----------
|
||||
// Testbench for the UDS core.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module tb_uds();
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Internal constant and parameter definitions.
|
||||
//----------------------------------------------------------------
|
||||
parameter DEBUG = 1;
|
||||
parameter DUMP_WAIT = 0;
|
||||
|
||||
parameter CLK_HALF_PERIOD = 1;
|
||||
parameter CLK_PERIOD = 2 * CLK_HALF_PERIOD;
|
||||
|
||||
localparam ADDR_NAME0 = 8'h00;
|
||||
localparam ADDR_NAME1 = 8'h01;
|
||||
localparam ADDR_VERSION = 8'h02;
|
||||
|
||||
localparam ADDR_UDS_FIRST = 8'h10;
|
||||
localparam ADDR_UDS_LAST = 8'h17;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Register and Wire declarations.
|
||||
//----------------------------------------------------------------
|
||||
reg [31 : 0] cycle_ctr;
|
||||
reg [31 : 0] error_ctr;
|
||||
reg [31 : 0] tc_ctr;
|
||||
reg tb_monitor;
|
||||
|
||||
reg tb_clk;
|
||||
reg tb_reset_n;
|
||||
reg tb_cs;
|
||||
reg [7 : 0] tb_address;
|
||||
wire [31 : 0] tb_read_data;
|
||||
|
||||
reg [31 : 0] read_data;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Device Under Test.
|
||||
//----------------------------------------------------------------
|
||||
uds dut(
|
||||
.clk(tb_clk),
|
||||
.reset_n(tb_reset_n),
|
||||
|
||||
.cs(tb_cs),
|
||||
|
||||
.address(tb_address),
|
||||
.read_data(tb_read_data)
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// clk_gen
|
||||
//
|
||||
// Always running clock generator process.
|
||||
//----------------------------------------------------------------
|
||||
always
|
||||
begin : clk_gen
|
||||
#CLK_HALF_PERIOD;
|
||||
tb_clk = !tb_clk;
|
||||
end // clk_gen
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// sys_monitor()
|
||||
//
|
||||
// An always running process that creates a cycle counter and
|
||||
// conditionally displays information about the DUT.
|
||||
//----------------------------------------------------------------
|
||||
always
|
||||
begin : sys_monitor
|
||||
cycle_ctr = cycle_ctr + 1;
|
||||
#(CLK_PERIOD);
|
||||
if (tb_monitor)
|
||||
begin
|
||||
dump_dut_state();
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// dump_dut_state()
|
||||
//
|
||||
// Dump the state of the dump when needed.
|
||||
//----------------------------------------------------------------
|
||||
task dump_dut_state;
|
||||
begin : dump_dut_state
|
||||
integer i;
|
||||
$display("State of DUT");
|
||||
$display("------------");
|
||||
$display("Cycle: %08d", cycle_ctr);
|
||||
for (i = 0 ; i < 8 ; i = i + 1) begin
|
||||
$display("uds_reg[%1d]: 0x%08x, uds_rd_reg[%1d]: 0x%1x",
|
||||
i, dut.uds_reg[i], i, dut.uds_rd_reg[i]);
|
||||
end
|
||||
$display("");
|
||||
$display("");
|
||||
end
|
||||
endtask // dump_dut_state
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// reset_dut()
|
||||
//
|
||||
// Toggle reset to put the DUT into a well known state.
|
||||
//----------------------------------------------------------------
|
||||
task reset_dut;
|
||||
begin
|
||||
$display("--- Toggle reset.");
|
||||
tb_reset_n = 0;
|
||||
#(2 * CLK_PERIOD);
|
||||
tb_reset_n = 1;
|
||||
end
|
||||
endtask // reset_dut
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// display_test_result()
|
||||
//
|
||||
// Display the accumulated test results.
|
||||
//----------------------------------------------------------------
|
||||
task display_test_result;
|
||||
begin
|
||||
if (error_ctr == 0)
|
||||
begin
|
||||
$display("--- All %02d test cases completed successfully", tc_ctr);
|
||||
end
|
||||
else
|
||||
begin
|
||||
$display("--- %02d tests completed - %02d test cases did not complete successfully.",
|
||||
tc_ctr, error_ctr);
|
||||
end
|
||||
end
|
||||
endtask // display_test_result
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// init_sim()
|
||||
//
|
||||
// Initialize all counters and testbed functionality as well
|
||||
// as setting the DUT inputs to defined values.
|
||||
//----------------------------------------------------------------
|
||||
task init_sim;
|
||||
begin
|
||||
cycle_ctr = 0;
|
||||
error_ctr = 0;
|
||||
tc_ctr = 0;
|
||||
tb_monitor = 0;
|
||||
|
||||
tb_clk = 1'h0;
|
||||
tb_reset_n = 1'h1;
|
||||
tb_cs = 1'h0;
|
||||
tb_address = 8'h0;
|
||||
end
|
||||
endtask // init_sim
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// read_word()
|
||||
//
|
||||
// Read a data word from the given address in the DUT.
|
||||
// the word read will be available in the global variable
|
||||
// read_data.
|
||||
//----------------------------------------------------------------
|
||||
task read_word(input [11 : 0] address);
|
||||
begin
|
||||
tb_address = address;
|
||||
tb_cs = 1;
|
||||
#(CLK_PERIOD);
|
||||
read_data = tb_read_data;
|
||||
tb_cs = 0;
|
||||
|
||||
if (DEBUG)
|
||||
begin
|
||||
$display("--- Reading 0x%08x from 0x%02x.", read_data, address);
|
||||
$display("");
|
||||
end
|
||||
end
|
||||
endtask // read_word
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// test1()
|
||||
//----------------------------------------------------------------
|
||||
task test1;
|
||||
begin
|
||||
tc_ctr = tc_ctr + 1;
|
||||
tb_monitor = 1'h0;
|
||||
|
||||
$display("");
|
||||
$display("--- test1: started.");
|
||||
|
||||
$display("--- test1: Reading NAME and version info.");
|
||||
read_word(ADDR_NAME0);
|
||||
read_word(ADDR_NAME1);
|
||||
read_word(ADDR_VERSION);
|
||||
|
||||
$display("--- test1: Dumping DUT state to show UDS contents");
|
||||
dump_dut_state();
|
||||
|
||||
$display("--- test1: Reading UDS words.");
|
||||
read_word(ADDR_UDS_FIRST + 0);
|
||||
read_word(ADDR_UDS_FIRST + 1);
|
||||
read_word(ADDR_UDS_FIRST + 2);
|
||||
|
||||
$display("--- test1: Dumping state again to see read bits.");
|
||||
dump_dut_state();
|
||||
|
||||
$display("--- test1: Reading rest of the words.");
|
||||
read_word(ADDR_UDS_FIRST + 3);
|
||||
read_word(ADDR_UDS_FIRST + 4);
|
||||
read_word(ADDR_UDS_FIRST + 5);
|
||||
read_word(ADDR_UDS_FIRST + 6);
|
||||
read_word(ADDR_UDS_FIRST + 7);
|
||||
|
||||
$display("--- test1: Dumping state again to see read bits.");
|
||||
dump_dut_state();
|
||||
|
||||
$display("--- test1: Reading UDS words again.");
|
||||
read_word(ADDR_UDS_FIRST + 0);
|
||||
read_word(ADDR_UDS_FIRST + 1);
|
||||
read_word(ADDR_UDS_FIRST + 2);
|
||||
read_word(ADDR_UDS_FIRST + 3);
|
||||
read_word(ADDR_UDS_FIRST + 4);
|
||||
read_word(ADDR_UDS_FIRST + 5);
|
||||
read_word(ADDR_UDS_FIRST + 6);
|
||||
read_word(ADDR_UDS_FIRST + 7);
|
||||
|
||||
$display("--- test1: Resetting DUT.");
|
||||
reset_dut();
|
||||
|
||||
$display("--- test1: Dumping state again to see read bits.");
|
||||
dump_dut_state();
|
||||
|
||||
$display("--- test1: Reading UDS words.");
|
||||
read_word(ADDR_UDS_FIRST + 0);
|
||||
read_word(ADDR_UDS_FIRST + 1);
|
||||
read_word(ADDR_UDS_FIRST + 2);
|
||||
read_word(ADDR_UDS_FIRST + 3);
|
||||
read_word(ADDR_UDS_FIRST + 4);
|
||||
read_word(ADDR_UDS_FIRST + 5);
|
||||
read_word(ADDR_UDS_FIRST + 6);
|
||||
read_word(ADDR_UDS_FIRST + 7);
|
||||
|
||||
$display("--- test1: Reading UDS words again.");
|
||||
read_word(ADDR_UDS_FIRST + 0);
|
||||
read_word(ADDR_UDS_FIRST + 1);
|
||||
read_word(ADDR_UDS_FIRST + 2);
|
||||
read_word(ADDR_UDS_FIRST + 3);
|
||||
read_word(ADDR_UDS_FIRST + 4);
|
||||
read_word(ADDR_UDS_FIRST + 5);
|
||||
read_word(ADDR_UDS_FIRST + 6);
|
||||
read_word(ADDR_UDS_FIRST + 7);
|
||||
|
||||
$display("--- test1: completed.");
|
||||
$display("");
|
||||
end
|
||||
endtask // tes1
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// uds_test
|
||||
//----------------------------------------------------------------
|
||||
initial
|
||||
begin : uds_test
|
||||
$display("");
|
||||
$display(" -= Testbench for uds started =-");
|
||||
$display(" ===========================");
|
||||
$display("");
|
||||
|
||||
init_sim();
|
||||
reset_dut();
|
||||
test1();
|
||||
|
||||
display_test_result();
|
||||
$display("");
|
||||
$display(" -= Testbench for uds started =-");
|
||||
$display(" ===========================");
|
||||
$display("");
|
||||
$finish;
|
||||
end // uds_test
|
||||
endmodule // tb_uds
|
||||
|
||||
//======================================================================
|
||||
// EOF tb_uds.v
|
||||
//======================================================================
|
55
hw/application_fpga/core/uds/toolruns/Makefile
Executable file
55
hw/application_fpga/core/uds/toolruns/Makefile
Executable file
|
@ -0,0 +1,55 @@
|
|||
#===================================================================
|
||||
#
|
||||
# Makefile
|
||||
# --------
|
||||
# Makefile for building the UDS core.
|
||||
#
|
||||
#
|
||||
# Author: Joachim Strombergson
|
||||
# Copyright (C) 2022 - Tillitis AB
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
#===================================================================
|
||||
|
||||
TOP_SRC=../rtl/uds.v
|
||||
TB_TOP_SRC =../tb/tb_uds.v
|
||||
|
||||
CC = iverilog
|
||||
CC_FLAGS = -Wall
|
||||
|
||||
LINT = verilator
|
||||
LINT_FLAGS = +1364-2001ext+ --lint-only -Wall -Wno-fatal -Wno-DECLFILENAME
|
||||
|
||||
|
||||
all: top.sim
|
||||
|
||||
|
||||
top.sim: $(TB_TOP_SRC) $(TOP_SRC)
|
||||
$(CC) $(CC_FLAGS) -o top.sim $(TB_TOP_SRC) $(TOP_SRC)
|
||||
|
||||
|
||||
sim-top: top.sim
|
||||
./top.sim
|
||||
|
||||
|
||||
lint-top: $(TOP_SRC)
|
||||
$(LINT) $(LINT_FLAGS) $(TOP_SRC)
|
||||
|
||||
|
||||
clean:
|
||||
rm -f top.sim
|
||||
|
||||
|
||||
help:
|
||||
@echo "Build system for simulation of UDS core"
|
||||
@echo ""
|
||||
@echo "Supported targets:"
|
||||
@echo "------------------"
|
||||
@echo "top.sim: Build top level simulation target."
|
||||
@echo "sim-top: Run top level simulation."
|
||||
@echo "lint-top: Lint top rtl source files."
|
||||
@echo "clean: Delete all built files."
|
||||
|
||||
#===================================================================
|
||||
# EOF Makefile
|
||||
#===================================================================
|
39
hw/application_fpga/data/application_fpga_mta1_usb_dev.pcf
Normal file
39
hw/application_fpga/data/application_fpga_mta1_usb_dev.pcf
Normal file
|
@ -0,0 +1,39 @@
|
|||
#=======================================================================
|
||||
#
|
||||
# application_fpga_mta1_usb_dev.pcf
|
||||
# ---------------------------------
|
||||
# Pin constraints file for the Application FPGA design used on the
|
||||
# Mullvad MTA1_USB_DEV board.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 2022 - Tillitis AB
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
#=======================================================================
|
||||
|
||||
# UART.
|
||||
set_io interface_rx 15
|
||||
set_io interface_tx 16
|
||||
#set_io interface_cts 27
|
||||
#set_io interface_rts 28
|
||||
|
||||
|
||||
# Touch sense, mapped to a switch.
|
||||
set_io touch_event 23
|
||||
|
||||
|
||||
# GPIOs.
|
||||
set_io app_gpio1 26
|
||||
set_io app_gpio2 27
|
||||
set_io app_gpio3 31
|
||||
set_io app_gpio4 32
|
||||
|
||||
|
||||
# LEDs
|
||||
set_io led_r 41
|
||||
set_io led_g 40
|
||||
set_io led_b 39
|
||||
|
||||
#=======================================================================
|
||||
# EOF application_fpga_usb_dev.pcf
|
||||
#=======================================================================
|
39
hw/application_fpga/data/application_fpga_mta1_usb_v1.pcf
Normal file
39
hw/application_fpga/data/application_fpga_mta1_usb_v1.pcf
Normal file
|
@ -0,0 +1,39 @@
|
|||
#=======================================================================
|
||||
#
|
||||
# application_fpga_usb_v1.pcf
|
||||
# ---------------------------
|
||||
# Pin constraints file for the Application FPGA design to be used
|
||||
# on the MTA1-USB-V1 board with the CH552 MCU used as a USB-serial chip.
|
||||
#
|
||||
#
|
||||
# Copyright (C) 2022 - Tillitis AB
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
#=======================================================================
|
||||
|
||||
# UART.
|
||||
set_io interface_rx 26
|
||||
set_io interface_tx 25
|
||||
# set_io interface_cts 27
|
||||
# set_io interface_rts 28
|
||||
|
||||
|
||||
# Touch sense.
|
||||
set_io touch_event 6
|
||||
|
||||
|
||||
# GPIOs.
|
||||
set_io app_gpio1 36
|
||||
set_io app_gpio2 38
|
||||
set_io app_gpio3 45
|
||||
set_io app_gpio4 46
|
||||
|
||||
|
||||
# LEDs
|
||||
set_io led_r 39
|
||||
set_io led_g 40
|
||||
set_io led_b 41
|
||||
|
||||
#=======================================================================
|
||||
# EOF application_fpga_ch552.pcf
|
||||
#=======================================================================
|
2
hw/application_fpga/data/udi.hex
Normal file
2
hw/application_fpga/data/udi.hex
Normal file
|
@ -0,0 +1,2 @@
|
|||
00010203
|
||||
04050607
|
8
hw/application_fpga/data/uds.hex
Normal file
8
hw/application_fpga/data/uds.hex
Normal file
|
@ -0,0 +1,8 @@
|
|||
80808080
|
||||
91919191
|
||||
a2a2a2a2
|
||||
b3b3b3b3
|
||||
c4c4c4c4
|
||||
d5d5d5d5
|
||||
e6e6e6e6
|
||||
f7f7f7f7
|
15
hw/application_fpga/fw/.clang-format
Normal file
15
hw/application_fpga/fw/.clang-format
Normal file
|
@ -0,0 +1,15 @@
|
|||
BasedOnStyle: LLVM
|
||||
UseTab: Always
|
||||
IndentWidth: 8
|
||||
TabWidth: 8
|
||||
|
||||
IncludeBlocks: Preserve
|
||||
|
||||
BreakBeforeBraces: Custom
|
||||
BraceWrapping:
|
||||
AfterFunction: true
|
||||
|
||||
AllowShortFunctionsOnASingleLine: false
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortEnumsOnASingleLine: false
|
||||
|
4
hw/application_fpga/fw/mta1_mkdf/Makefile
Normal file
4
hw/application_fpga/fw/mta1_mkdf/Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
.PHONY: fmt
|
||||
fmt:
|
||||
# Uses ../.clang-format
|
||||
clang-format --verbose -i main.c lib.h lib.c proto.h proto.c types.h
|
53
hw/application_fpga/fw/mta1_mkdf/README.md
Normal file
53
hw/application_fpga/fw/mta1_mkdf/README.md
Normal file
|
@ -0,0 +1,53 @@
|
|||
# Tillitis Key firmware
|
||||
|
||||
## Build the firmware
|
||||
|
||||
You need Clang with 32 bit RISC-V support. You can check this with:
|
||||
|
||||
```
|
||||
$ llc --version|grep riscv32
|
||||
riscv32 - 32-bit RISC-V
|
||||
```
|
||||
|
||||
or just try building.
|
||||
|
||||
Build the FPGA bitstream with the firmware using `make` in the
|
||||
`hw/application_fpga` directory.
|
||||
|
||||
If your available `objcopy` and `size` commands is anything other than
|
||||
the default `llvm-objcopy-14` and `llvm-size-14` define `OBJCOPY` and
|
||||
`SIZE` to whatever they're called on your system.
|
||||
|
||||
## Using QEMU
|
||||
|
||||
Checkout the `mta1` branch of [our version of the
|
||||
qemu](https://github.com/tillitis/qemu) and build:
|
||||
|
||||
```
|
||||
$ git clone -b mta1 https://github.com/tillitis/qemu
|
||||
$ mkdir qemu/build
|
||||
$ cd qemu/build
|
||||
$ ../configure --target-list=riscv32-softmmu
|
||||
$ make -j $(nproc)
|
||||
```
|
||||
|
||||
Run it like this:
|
||||
|
||||
```
|
||||
$ /path/to/qemu/build/qemu-system-riscv32 -nographic -M mta1_mkdf,fifo=chrid -bios firmware \
|
||||
-chardev pty,id=chrid
|
||||
```
|
||||
|
||||
This attaches the FIFO to a tty, something like `/dev/pts/16` which
|
||||
you can use with host software to talk to the firmware.
|
||||
|
||||
To quit QEMU you can use: `Ctrl-a x` (see `Ctrl-a ?` for other commands).
|
||||
|
||||
Debugging? Use the HTIF console by removing `-DNOCONSOLE` from the
|
||||
`CFLAGS` and using the helper functions in `lib.c` for printf-like
|
||||
debugging.
|
||||
|
||||
You can also use the qemu monitor for debugging, e.g. `info
|
||||
registers`, or run qemu with `-d in_asm` or `-d trace:riscv_trap`.
|
||||
|
||||
Happy hacking!
|
116
hw/application_fpga/fw/mta1_mkdf/blake2s/LICENSE
Normal file
116
hw/application_fpga/fw/mta1_mkdf/blake2s/LICENSE
Normal file
|
@ -0,0 +1,116 @@
|
|||
CC0 1.0 Universal
|
||||
|
||||
Statement of Purpose
|
||||
|
||||
The laws of most jurisdictions throughout the world automatically confer
|
||||
exclusive Copyright and Related Rights (defined below) upon the creator and
|
||||
subsequent owner(s) (each and all, an "owner") of an original work of
|
||||
authorship and/or a database (each, a "Work").
|
||||
|
||||
Certain owners wish to permanently relinquish those rights to a Work for the
|
||||
purpose of contributing to a commons of creative, cultural and scientific
|
||||
works ("Commons") that the public can reliably and without fear of later
|
||||
claims of infringement build upon, modify, incorporate in other works, reuse
|
||||
and redistribute as freely as possible in any form whatsoever and for any
|
||||
purposes, including without limitation commercial purposes. These owners may
|
||||
contribute to the Commons to promote the ideal of a free culture and the
|
||||
further production of creative, cultural and scientific works, or to gain
|
||||
reputation or greater distribution for their Work in part through the use and
|
||||
efforts of others.
|
||||
|
||||
For these and/or other purposes and motivations, and without any expectation
|
||||
of additional consideration or compensation, the person associating CC0 with a
|
||||
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
|
||||
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
|
||||
and publicly distribute the Work under its terms, with knowledge of his or her
|
||||
Copyright and Related Rights in the Work and the meaning and intended legal
|
||||
effect of CC0 on those rights.
|
||||
|
||||
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||
protected by copyright and related or neighboring rights ("Copyright and
|
||||
Related Rights"). Copyright and Related Rights include, but are not limited
|
||||
to, the following:
|
||||
|
||||
i. the right to reproduce, adapt, distribute, perform, display, communicate,
|
||||
and translate a Work;
|
||||
|
||||
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||
|
||||
iii. publicity and privacy rights pertaining to a person's image or likeness
|
||||
depicted in a Work;
|
||||
|
||||
iv. rights protecting against unfair competition in regards to a Work,
|
||||
subject to the limitations in paragraph 4(a), below;
|
||||
|
||||
v. rights protecting the extraction, dissemination, use and reuse of data in
|
||||
a Work;
|
||||
|
||||
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||
European Parliament and of the Council of 11 March 1996 on the legal
|
||||
protection of databases, and under any national implementation thereof,
|
||||
including any amended or successor version of such directive); and
|
||||
|
||||
vii. other similar, equivalent or corresponding rights throughout the world
|
||||
based on applicable law or treaty, and any national implementations thereof.
|
||||
|
||||
2. Waiver. To the greatest extent permitted by, but not in contravention of,
|
||||
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
|
||||
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
|
||||
and Related Rights and associated claims and causes of action, whether now
|
||||
known or unknown (including existing as well as future claims and causes of
|
||||
action), in the Work (i) in all territories worldwide, (ii) for the maximum
|
||||
duration provided by applicable law or treaty (including future time
|
||||
extensions), (iii) in any current or future medium and for any number of
|
||||
copies, and (iv) for any purpose whatsoever, including without limitation
|
||||
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
|
||||
the Waiver for the benefit of each member of the public at large and to the
|
||||
detriment of Affirmer's heirs and successors, fully intending that such Waiver
|
||||
shall not be subject to revocation, rescission, cancellation, termination, or
|
||||
any other legal or equitable action to disrupt the quiet enjoyment of the Work
|
||||
by the public as contemplated by Affirmer's express Statement of Purpose.
|
||||
|
||||
3. Public License Fallback. Should any part of the Waiver for any reason be
|
||||
judged legally invalid or ineffective under applicable law, then the Waiver
|
||||
shall be preserved to the maximum extent permitted taking into account
|
||||
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
|
||||
is so judged Affirmer hereby grants to each affected person a royalty-free,
|
||||
non transferable, non sublicensable, non exclusive, irrevocable and
|
||||
unconditional license to exercise Affirmer's Copyright and Related Rights in
|
||||
the Work (i) in all territories worldwide, (ii) for the maximum duration
|
||||
provided by applicable law or treaty (including future time extensions), (iii)
|
||||
in any current or future medium and for any number of copies, and (iv) for any
|
||||
purpose whatsoever, including without limitation commercial, advertising or
|
||||
promotional purposes (the "License"). The License shall be deemed effective as
|
||||
of the date CC0 was applied by Affirmer to the Work. Should any part of the
|
||||
License for any reason be judged legally invalid or ineffective under
|
||||
applicable law, such partial invalidity or ineffectiveness shall not
|
||||
invalidate the remainder of the License, and in such case Affirmer hereby
|
||||
affirms that he or she will not (i) exercise any of his or her remaining
|
||||
Copyright and Related Rights in the Work or (ii) assert any associated claims
|
||||
and causes of action with respect to the Work, in either case contrary to
|
||||
Affirmer's express Statement of Purpose.
|
||||
|
||||
4. Limitations and Disclaimers.
|
||||
|
||||
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||
surrendered, licensed or otherwise affected by this document.
|
||||
|
||||
b. Affirmer offers the Work as-is and makes no representations or warranties
|
||||
of any kind concerning the Work, express, implied, statutory or otherwise,
|
||||
including without limitation warranties of title, merchantability, fitness
|
||||
for a particular purpose, non infringement, or the absence of latent or
|
||||
other defects, accuracy, or the present or absence of errors, whether or not
|
||||
discoverable, all to the greatest extent permissible under applicable law.
|
||||
|
||||
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||
that may apply to the Work or any use thereof, including without limitation
|
||||
any person's Copyright and Related Rights in the Work. Further, Affirmer
|
||||
disclaims responsibility for obtaining any necessary consents, permissions
|
||||
or other rights required for any use of the Work.
|
||||
|
||||
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||
party to this document and has no duty or obligation with respect to this
|
||||
CC0 or use of the Work.
|
||||
|
||||
For more information, please see
|
||||
<http://creativecommons.org/publicdomain/zero/1.0/>
|
11
hw/application_fpga/fw/mta1_mkdf/blake2s/README.md
Normal file
11
hw/application_fpga/fw/mta1_mkdf/blake2s/README.md
Normal file
|
@ -0,0 +1,11 @@
|
|||
# blake2s
|
||||
|
||||
A Blake2s implementation taken from Joachim Strömbergson's
|
||||
|
||||
https://github.com/secworks/blake2s
|
||||
|
||||
Specifically from
|
||||
|
||||
https://github.com/secworks/blake2s/tree/master/src/model
|
||||
|
||||
Minor local changes for build purposes.
|
352
hw/application_fpga/fw/mta1_mkdf/blake2s/blake2s.c
Normal file
352
hw/application_fpga/fw/mta1_mkdf/blake2s/blake2s.c
Normal file
|
@ -0,0 +1,352 @@
|
|||
//======================================================================
|
||||
//
|
||||
// blake2s.c
|
||||
// ---------
|
||||
//
|
||||
// A simple blake2s Reference Implementation.
|
||||
//======================================================================
|
||||
|
||||
#include "../types.h"
|
||||
#include "../lib.h"
|
||||
#include "blake2s.h"
|
||||
|
||||
// Dummy printf() for verbose mode
|
||||
static void printf(const char *format, ...)
|
||||
{
|
||||
}
|
||||
|
||||
#define VERBOSE 0
|
||||
#define SHOW_V 0
|
||||
#define SHOW_M_WORDS 0
|
||||
|
||||
|
||||
// Cyclic right rotation.
|
||||
#ifndef ROTR32
|
||||
#define ROTR32(x, y) (((x) >> (y)) ^ ((x) << (32 - (y))))
|
||||
#endif
|
||||
|
||||
|
||||
// Little-endian byte access.
|
||||
#define B2S_GET32(p) \
|
||||
(((uint32_t) ((uint8_t *) (p))[0]) ^ \
|
||||
(((uint32_t) ((uint8_t *) (p))[1]) << 8) ^ \
|
||||
(((uint32_t) ((uint8_t *) (p))[2]) << 16) ^ \
|
||||
(((uint32_t) ((uint8_t *) (p))[3]) << 24))
|
||||
|
||||
|
||||
// Initialization Vector.
|
||||
static const uint32_t blake2s_iv[8] = {
|
||||
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
||||
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
|
||||
};
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
//------------------------------------------------------------------
|
||||
void print_v(uint32_t *v) {
|
||||
printf("0x%08x, 0x%08x, 0x%08x, 0x%08x\n", v[0], v[1], v[2], v[3]);
|
||||
printf("0x%08x, 0x%08x, 0x%08x, 0x%08x\n", v[4], v[5], v[6], v[7]);
|
||||
printf("0x%08x, 0x%08x, 0x%08x, 0x%08x\n", v[8], v[9], v[10], v[11]);
|
||||
printf("0x%08x, 0x%08x, 0x%08x, 0x%08x\n", v[12], v[13], v[14], v[15]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// print_ctx()
|
||||
// Print the contents of the context data structure.
|
||||
//------------------------------------------------------------------
|
||||
void print_ctx(blake2s_ctx *ctx) {
|
||||
printf("Chained state (h):\n");
|
||||
printf("0x%08x, 0x%08x, 0x%08x, 0x%08x, ",
|
||||
ctx->h[0], ctx->h[1], ctx->h[2], ctx->h[3]);
|
||||
printf("0x%08x, 0x%08x, 0x%08x, 0x%08x",
|
||||
ctx->h[4], ctx->h[5], ctx->h[6], ctx->h[7]);
|
||||
printf("\n");
|
||||
|
||||
printf("Byte counter (t):\n");
|
||||
printf("0x%08x, 0x%08x", ctx->t[0], ctx->t[1]);
|
||||
printf("\n");
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// B2S_G macro redefined as a G function.
|
||||
// Allows us to output intermediate values for debugging.
|
||||
//------------------------------------------------------------------
|
||||
void G(uint32_t *v, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t y) {
|
||||
if (VERBOSE) {
|
||||
printf("G started.\n");
|
||||
}
|
||||
|
||||
if (SHOW_V) {
|
||||
printf("v before processing:\n");
|
||||
print_v(&v[0]);
|
||||
}
|
||||
|
||||
if (SHOW_M_WORDS) {
|
||||
printf("x: 0x%08x, y: 0x%08x\n", x, y);
|
||||
}
|
||||
|
||||
v[a] = v[a] + v[b] + x;
|
||||
v[d] = ROTR32(v[d] ^ v[a], 16);
|
||||
v[c] = v[c] + v[d];
|
||||
v[b] = ROTR32(v[b] ^ v[c], 12);
|
||||
v[a] = v[a] + v[b] + y;
|
||||
v[d] = ROTR32(v[d] ^ v[a], 8);
|
||||
v[c] = v[c] + v[d];
|
||||
v[b] = ROTR32(v[b] ^ v[c], 7);
|
||||
|
||||
if (SHOW_V) {
|
||||
printf("v after processing:\n");
|
||||
print_v(&v[0]);
|
||||
}
|
||||
|
||||
if (VERBOSE) {
|
||||
printf("G completed.\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Compression function. "last" flag indicates last block.
|
||||
//------------------------------------------------------------------
|
||||
static void blake2s_compress(blake2s_ctx *ctx, int last)
|
||||
{
|
||||
const uint8_t sigma[10][16] = {
|
||||
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
|
||||
{11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
|
||||
{7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8},
|
||||
{9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13},
|
||||
{2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9},
|
||||
{12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
|
||||
{13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10},
|
||||
{6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5},
|
||||
{10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}
|
||||
};
|
||||
|
||||
int i;
|
||||
uint32_t v[16], m[16];
|
||||
|
||||
if (VERBOSE) {
|
||||
printf("blake2s_compress started.\n");
|
||||
}
|
||||
|
||||
// init work variables
|
||||
for (i = 0; i < 8; i++) {
|
||||
v[i] = ctx->h[i];
|
||||
v[i + 8] = blake2s_iv[i];
|
||||
}
|
||||
|
||||
// low 32 bits of offset
|
||||
// high 32 bits
|
||||
if (VERBOSE) {
|
||||
printf("t[0]: 0x%08x, t[1]: 0x%08x\n", ctx->t[0], ctx->t[1]);
|
||||
}
|
||||
v[12] ^= ctx->t[0];
|
||||
v[13] ^= ctx->t[1];
|
||||
|
||||
// last block flag set ?
|
||||
if (last) {
|
||||
v[14] = ~v[14];
|
||||
}
|
||||
|
||||
// get little-endian words
|
||||
for (i = 0; i < 16; i++) {
|
||||
m[i] = B2S_GET32(&ctx->b[4 * i]);
|
||||
}
|
||||
|
||||
if (VERBOSE) {
|
||||
printf("v before G processing:\n");
|
||||
print_v(&v[0]);
|
||||
}
|
||||
|
||||
// Ten rounds of the G function applied on rows, diagonal.
|
||||
for (i = 0; i < 10; i++) {
|
||||
if (VERBOSE) {
|
||||
printf("Round %02d:\n", (i + 1));
|
||||
printf("Row processing started.\n");
|
||||
}
|
||||
|
||||
G(&v[0], 0, 4, 8, 12, m[sigma[i][ 0]], m[sigma[i][ 1]]);
|
||||
G(&v[0], 1, 5, 9, 13, m[sigma[i][ 2]], m[sigma[i][ 3]]);
|
||||
G(&v[0], 2, 6, 10, 14, m[sigma[i][ 4]], m[sigma[i][ 5]]);
|
||||
G(&v[0], 3, 7, 11, 15, m[sigma[i][ 6]], m[sigma[i][ 7]]);
|
||||
|
||||
if (VERBOSE) {
|
||||
printf("Row processing completed.\n");
|
||||
printf("Diagonal processing started.\n");
|
||||
}
|
||||
|
||||
G(&v[0], 0, 5, 10, 15, m[sigma[i][ 8]], m[sigma[i][ 9]]);
|
||||
G(&v[0], 1, 6, 11, 12, m[sigma[i][10]], m[sigma[i][11]]);
|
||||
G(&v[0], 2, 7, 8, 13, m[sigma[i][12]], m[sigma[i][13]]);
|
||||
G(&v[0], 3, 4, 9, 14, m[sigma[i][14]], m[sigma[i][15]]);
|
||||
|
||||
if (VERBOSE) {
|
||||
printf("Diagonal processing completed.\n");
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (VERBOSE) {
|
||||
printf("v after G processing:\n");
|
||||
print_v(&v[0]);
|
||||
}
|
||||
|
||||
// Update the hash state.
|
||||
for (i = 0; i < 8; ++i) {
|
||||
ctx->h[i] ^= v[i] ^ v[i + 8];
|
||||
}
|
||||
|
||||
if (VERBOSE) {
|
||||
printf("blake2s_compress completed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Initialize the hashing context "ctx" with optional key "key".
|
||||
// 1 <= outlen <= 32 gives the digest size in bytes.
|
||||
// Secret key (also <= 32 bytes) is optional (keylen = 0).
|
||||
//------------------------------------------------------------------
|
||||
int blake2s_init(blake2s_ctx *ctx, size_t outlen,
|
||||
const void *key, size_t keylen) // (keylen=0: no key)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (VERBOSE) {
|
||||
printf("blake2s_init started.\n");
|
||||
printf("Context before blake2s_init processing:\n");
|
||||
print_ctx(ctx);
|
||||
}
|
||||
|
||||
if (outlen == 0 || outlen > 32 || keylen > 32)
|
||||
return -1; // illegal parameters
|
||||
|
||||
for (i = 0; i < 8; i++) // state, "param block"
|
||||
ctx->h[i] = blake2s_iv[i];
|
||||
ctx->h[0] ^= 0x01010000 ^ (keylen << 8) ^ outlen;
|
||||
|
||||
ctx->t[0] = 0; // input count low word
|
||||
ctx->t[1] = 0; // input count high word
|
||||
ctx->c = 0; // pointer within buffer
|
||||
ctx->outlen = outlen;
|
||||
|
||||
for (i = keylen; i < 64; i++) // zero input block
|
||||
ctx->b[i] = 0;
|
||||
if (keylen > 0) {
|
||||
blake2s_update(ctx, key, keylen);
|
||||
ctx->c = 64; // at the end
|
||||
}
|
||||
|
||||
if (VERBOSE) {
|
||||
printf("Context after blake2s_init processing:\n");
|
||||
print_ctx(ctx);
|
||||
printf("blake2s_init completed.\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Add "inlen" bytes from "in" into the hash.
|
||||
//------------------------------------------------------------------
|
||||
void blake2s_update(blake2s_ctx *ctx,
|
||||
const void *in, size_t inlen) // data bytes
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (VERBOSE) {
|
||||
printf("blake2s_update started.\n");
|
||||
printf("Context before blake2s_update processing:\n");
|
||||
print_ctx(ctx);
|
||||
}
|
||||
|
||||
for (i = 0; i < inlen; i++) {
|
||||
if (ctx->c == 64) { // buffer full ?
|
||||
ctx->t[0] += ctx->c; // add counters
|
||||
if (ctx->t[0] < ctx->c) // carry overflow ?
|
||||
ctx->t[1]++; // high word
|
||||
blake2s_compress(ctx, 0); // compress (not last)
|
||||
ctx->c = 0; // counter to zero
|
||||
}
|
||||
ctx->b[ctx->c++] = ((const uint8_t *) in)[i];
|
||||
}
|
||||
|
||||
if (VERBOSE) {
|
||||
printf("Context after blake2s_update processing:\n");
|
||||
print_ctx(ctx);
|
||||
printf("blake2s_update completed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Generate the message digest (size given in init).
|
||||
// Result placed in "out".
|
||||
//------------------------------------------------------------------
|
||||
void blake2s_final(blake2s_ctx *ctx, void *out)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (VERBOSE) {
|
||||
printf("blake2s_final started.\n");
|
||||
printf("Context before blake2s_final processing:\n");
|
||||
print_ctx(ctx);
|
||||
}
|
||||
|
||||
ctx->t[0] += ctx->c; // mark last block offset
|
||||
|
||||
// carry overflow
|
||||
// high word
|
||||
if (ctx->t[0] < ctx->c) {
|
||||
ctx->t[1]++;
|
||||
}
|
||||
|
||||
// fill up with zeros
|
||||
// final block flag = 1
|
||||
while (ctx->c < 64) {
|
||||
ctx->b[ctx->c++] = 0;
|
||||
}
|
||||
blake2s_compress(ctx, 1);
|
||||
|
||||
// little endian convert and store
|
||||
for (i = 0; i < ctx->outlen; i++) {
|
||||
((uint8_t *) out)[i] =
|
||||
(ctx->h[i >> 2] >> (8 * (i & 3))) & 0xFF;
|
||||
}
|
||||
|
||||
if (VERBOSE) {
|
||||
printf("Context after blake2s_final processing:\n");
|
||||
print_ctx(ctx);
|
||||
printf("blake2s_final completed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------
|
||||
// Convenience function for all-in-one computation.
|
||||
//------------------------------------------------------------------
|
||||
int blake2s(void *out, size_t outlen,
|
||||
const void *key, size_t keylen,
|
||||
const void *in, size_t inlen)
|
||||
{
|
||||
blake2s_ctx ctx;
|
||||
|
||||
if (blake2s_init(&ctx, outlen, key, keylen))
|
||||
return -1;
|
||||
|
||||
blake2s_update(&ctx, in, inlen);
|
||||
|
||||
blake2s_final(&ctx, out);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//======================================================================
|
||||
//======================================================================
|
38
hw/application_fpga/fw/mta1_mkdf/blake2s/blake2s.h
Normal file
38
hw/application_fpga/fw/mta1_mkdf/blake2s/blake2s.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
// blake2s.h
|
||||
// BLAKE2s Hashing Context and API Prototypes
|
||||
|
||||
#ifndef BLAKE2S_H
|
||||
#define BLAKE2S_H
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
// state context
|
||||
typedef struct {
|
||||
uint8_t b[64]; // input buffer
|
||||
uint32_t h[8]; // chained state
|
||||
uint32_t t[2]; // total number of bytes
|
||||
size_t c; // pointer for b[]
|
||||
size_t outlen; // digest size
|
||||
} blake2s_ctx;
|
||||
|
||||
// Initialize the hashing context "ctx" with optional key "key".
|
||||
// 1 <= outlen <= 32 gives the digest size in bytes.
|
||||
// Secret key (also <= 32 bytes) is optional (keylen = 0).
|
||||
int blake2s_init(blake2s_ctx *ctx, size_t outlen,
|
||||
const void *key, size_t keylen); // secret key
|
||||
|
||||
// Add "inlen" bytes from "in" into the hash.
|
||||
void blake2s_update(blake2s_ctx *ctx, // context
|
||||
const void *in, size_t inlen); // data to be hashed
|
||||
|
||||
// Generate the message digest (size given in init).
|
||||
// Result placed in "out".
|
||||
void blake2s_final(blake2s_ctx *ctx, void *out);
|
||||
|
||||
// All-in-one convenience function.
|
||||
int blake2s(void *out, size_t outlen, // return buffer for digest
|
||||
const void *key, size_t keylen, // optional secret key
|
||||
const void *in, size_t inlen); // data to be hashed
|
||||
|
||||
#endif
|
||||
|
71
hw/application_fpga/fw/mta1_mkdf/firmware.lds
Normal file
71
hw/application_fpga/fw/mta1_mkdf/firmware.lds
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright (C) 2022 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
OUTPUT_ARCH( "riscv" )
|
||||
ENTRY(_start)
|
||||
|
||||
MEMORY
|
||||
{
|
||||
/* TODO ROM size should be adjusted, RAM should be ok. */
|
||||
ROM (rx) : ORIGIN = 0x00000000, LENGTH = 0x20000 /* 128 KB */
|
||||
RAM (rwx) : ORIGIN = 0x40000000, LENGTH = 0x20000 /* 128 KB */
|
||||
}
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
.text.init :
|
||||
{
|
||||
*(.text.init)
|
||||
} >ROM
|
||||
|
||||
.htif :
|
||||
{
|
||||
. = ALIGN(0x00000000);
|
||||
*(.htif)
|
||||
} >ROM
|
||||
|
||||
.text :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
*(.text) /* .text sections (code) */
|
||||
*(.text*) /* .text* sections (code) */
|
||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
||||
*(.srodata) /* .rodata sections (constants, strings, etc.) */
|
||||
*(.srodata*) /* .rodata* sections (constants, strings, etc.) */
|
||||
. = ALIGN(4);
|
||||
_etext = .;
|
||||
_sidata = _etext;
|
||||
} >ROM
|
||||
|
||||
.data : AT (_etext)
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sdata = .;
|
||||
. = ALIGN(4);
|
||||
*(.data) /* .data sections */
|
||||
*(.data*) /* .data* sections */
|
||||
*(.sdata) /* .sdata sections */
|
||||
*(.sdata*) /* .sdata* sections */
|
||||
. = ALIGN(4);
|
||||
_edata = .;
|
||||
} >RAM
|
||||
|
||||
/* Uninitialized data section */
|
||||
.bss :
|
||||
{
|
||||
. = ALIGN(4);
|
||||
_sbss = .;
|
||||
*(.bss)
|
||||
*(.bss*)
|
||||
*(.sbss)
|
||||
*(.sbss*)
|
||||
*(COMMON)
|
||||
|
||||
. = ALIGN(4);
|
||||
_ebss = .;
|
||||
} >RAM
|
||||
/* Init stack to _ebss + size */
|
||||
}
|
171
hw/application_fpga/fw/mta1_mkdf/lib.c
Normal file
171
hw/application_fpga/fw/mta1_mkdf/lib.c
Normal file
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright (C) 2022 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "lib.h"
|
||||
#include "types.h"
|
||||
|
||||
#if NOCONSOLE
|
||||
void putc(int ch)
|
||||
{
|
||||
}
|
||||
|
||||
void lf()
|
||||
{
|
||||
}
|
||||
|
||||
void puthex(uint8_t c)
|
||||
{
|
||||
}
|
||||
|
||||
void putinthex(const uint32_t n)
|
||||
{
|
||||
}
|
||||
|
||||
int puts(const char *s)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hexdump(uint8_t *buf, int len)
|
||||
{
|
||||
}
|
||||
|
||||
#else
|
||||
struct {
|
||||
uint32_t arr[2];
|
||||
} volatile tohost __attribute__((section(".htif")));
|
||||
struct {
|
||||
uint32_t arr[2];
|
||||
} volatile fromhost __attribute__((section(".htif")));
|
||||
|
||||
static void htif_send(uint8_t dev, uint8_t cmd, int64_t data)
|
||||
{
|
||||
/* endian neutral encoding with ordered 32-bit writes */
|
||||
union {
|
||||
uint32_t arr[2];
|
||||
uint64_t val;
|
||||
} encode = {.val = (uint64_t)dev << 56 | (uint64_t)cmd << 48 | data};
|
||||
tohost.arr[0] = encode.arr[0];
|
||||
tohost.arr[1] = encode.arr[1];
|
||||
}
|
||||
|
||||
static void htif_set_tohost(uint8_t dev, uint8_t cmd, int64_t data)
|
||||
{
|
||||
/* send data with specified device and command */
|
||||
while (tohost.arr[0]) {
|
||||
asm volatile("" : : "r"(fromhost.arr[0]));
|
||||
asm volatile("" : : "r"(fromhost.arr[1]));
|
||||
}
|
||||
htif_send(dev, cmd, data);
|
||||
}
|
||||
|
||||
static int htif_putchar(int ch)
|
||||
{
|
||||
htif_set_tohost(1, 1, ch & 0xff);
|
||||
return ch & 0xff;
|
||||
}
|
||||
|
||||
int puts(const char *s)
|
||||
{
|
||||
while (*s)
|
||||
htif_putchar(*s++);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hexdump(uint8_t *buf, int len)
|
||||
{
|
||||
uint8_t *row;
|
||||
uint8_t *byte;
|
||||
uint8_t *max;
|
||||
|
||||
row = buf;
|
||||
max = &buf[len];
|
||||
for (byte = 0; byte != max; row = byte) {
|
||||
for (byte = row; byte != max && byte != (row + 16); byte++) {
|
||||
puthex(*byte);
|
||||
}
|
||||
|
||||
lf();
|
||||
}
|
||||
}
|
||||
|
||||
void putc(int ch)
|
||||
{
|
||||
htif_putchar(ch);
|
||||
}
|
||||
|
||||
void lf()
|
||||
{
|
||||
htif_putchar('\n');
|
||||
}
|
||||
|
||||
void puthex(uint8_t c)
|
||||
{
|
||||
unsigned int upper = (c >> 4) & 0xf;
|
||||
unsigned int lower = c & 0xf;
|
||||
|
||||
htif_putchar(upper < 10 ? '0' + upper : 'a' - 10 + upper);
|
||||
htif_putchar(lower < 10 ? '0' + lower : 'a' - 10 + lower);
|
||||
}
|
||||
|
||||
void putinthex(const uint32_t n)
|
||||
{
|
||||
uint8_t buf[4];
|
||||
|
||||
memcpy(buf, &n, 4);
|
||||
puts("0x");
|
||||
for (int i = 3; i > -1; i--) {
|
||||
puthex(buf[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void *memset(void *dest, int c, unsigned n)
|
||||
{
|
||||
uint8_t *s = dest;
|
||||
|
||||
for (; n; n--, s++)
|
||||
*s = c;
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
__attribute__((used)) void *memcpy(void *dest, const void *src, unsigned n)
|
||||
{
|
||||
uint8_t *src_byte = (uint8_t *)src;
|
||||
uint8_t *dest_byte = (uint8_t *)dest;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
dest_byte[i] = src_byte[i];
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
__attribute__((used)) void *wordcpy(void *dest, const void *src, unsigned n)
|
||||
{
|
||||
uint32_t *src_word = (uint32_t *)src;
|
||||
uint32_t *dest_word = (uint32_t *)dest;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
dest_word[i] = src_word[i];
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
|
||||
int memeq(void *dest, const void *src, unsigned n)
|
||||
{
|
||||
uint8_t *src_byte = (uint8_t *)src;
|
||||
uint8_t *dest_byte = (uint8_t *)dest;
|
||||
|
||||
for (int i = 0; i < n; i++) {
|
||||
if (dest_byte[i] != src_byte[i]) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
22
hw/application_fpga/fw/mta1_mkdf/lib.h
Normal file
22
hw/application_fpga/fw/mta1_mkdf/lib.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (C) 2022 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef LIB_H
|
||||
#define LIB_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
void putc(int ch);
|
||||
void lf();
|
||||
void puthex(uint8_t c);
|
||||
void putinthex(const uint32_t n);
|
||||
int puts(const char *s);
|
||||
void hexdump(uint8_t *buf, int len);
|
||||
void *memset(void *dest, int c, unsigned n);
|
||||
void *memcpy(void *dest, const void *src, unsigned n);
|
||||
void *wordcpy(void *dest, const void *src, unsigned n);
|
||||
int memeq(void *dest, const void *src, unsigned n);
|
||||
|
||||
#endif
|
262
hw/application_fpga/fw/mta1_mkdf/main.c
Normal file
262
hw/application_fpga/fw/mta1_mkdf/main.c
Normal file
|
@ -0,0 +1,262 @@
|
|||
/*
|
||||
* Copyright (C) 2022 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "../mta1_mkdf_mem.h"
|
||||
#include "blake2s/blake2s.h"
|
||||
#include "lib.h"
|
||||
#include "proto.h"
|
||||
#include "types.h"
|
||||
|
||||
// In RAM + above the stack (0x40010000)
|
||||
#define APP_RAM_ADDR (MTA1_MKDF_RAM_BASE + 0x10000)
|
||||
#define APP_MAX_SIZE 65536
|
||||
|
||||
// clang-format off
|
||||
static volatile uint32_t *uds = (volatile uint32_t *)MTA1_MKDF_MMIO_UDS_FIRST;
|
||||
static volatile uint32_t *switch_app = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_SWITCH_APP;
|
||||
static volatile uint32_t *name0 = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_NAME0;
|
||||
static volatile uint32_t *name1 = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_NAME1;
|
||||
static volatile uint32_t *ver = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_VERSION;
|
||||
static volatile uint32_t *cdi = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_CDI_FIRST;
|
||||
static volatile uint32_t *app_addr = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_APP_ADDR;
|
||||
static volatile uint32_t *app_size = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_APP_SIZE;
|
||||
|
||||
#define LED_RED (1 << MTA1_MKDF_MMIO_MTA1_LED_R_BIT)
|
||||
#define LED_GREEN (1 << MTA1_MKDF_MMIO_MTA1_LED_G_BIT)
|
||||
#define LED_BLUE (1 << MTA1_MKDF_MMIO_MTA1_LED_B_BIT)
|
||||
|
||||
static void print_hw_version(uint32_t name0, uint32_t name1, uint32_t ver)
|
||||
{
|
||||
puts("Hello, I'm ");
|
||||
putc(name0 >> 24);
|
||||
putc(name0 >> 16);
|
||||
putc(name0 >> 8);
|
||||
putc(name0);
|
||||
|
||||
putc('-');
|
||||
|
||||
putc(name1 >> 24);
|
||||
putc(name1 >> 16);
|
||||
putc(name1 >> 8);
|
||||
putc(name1);
|
||||
|
||||
putc(' ');
|
||||
putinthex(ver);
|
||||
lf();
|
||||
}
|
||||
// clang-format on
|
||||
|
||||
static void print_digest(uint8_t *md)
|
||||
{
|
||||
puts("The app digest:\n");
|
||||
for (int j = 0; j < 4; j++) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
puthex(md[i + 8 * j]);
|
||||
}
|
||||
lf();
|
||||
}
|
||||
lf();
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
uint32_t local_name0 = *name0;
|
||||
uint32_t local_name1 = *name1;
|
||||
uint32_t local_ver = *ver;
|
||||
struct frame_header hdr; // Used in both directions
|
||||
uint8_t cmd[CMDLEN_MAXBYTES];
|
||||
uint8_t rsp[CMDLEN_MAXBYTES];
|
||||
uint8_t *loadaddr = (uint8_t *)APP_RAM_ADDR;
|
||||
int left = 0; // Bytes left to read
|
||||
int nbytes = 0; // Bytes to write to memory
|
||||
uint32_t local_app_size = 0;
|
||||
uint8_t in;
|
||||
uint8_t digest[32];
|
||||
|
||||
print_hw_version(local_name0, local_name1, local_ver);
|
||||
|
||||
for (;;) {
|
||||
// blocking; fw flashing white while waiting for cmd
|
||||
in = readbyte_ledflash(LED_RED | LED_BLUE | LED_GREEN, 500000);
|
||||
|
||||
if (parseframe(in, &hdr) == -1) {
|
||||
puts("Couldn't parse header\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
memset(cmd, 0, CMDLEN_MAXBYTES);
|
||||
// Read firmware command: Blocks!
|
||||
read(cmd, hdr.len);
|
||||
|
||||
// Is it for us?
|
||||
if (hdr.endpoint != DST_FW) {
|
||||
puts("Message not meant for us\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Reset response buffer
|
||||
memset(rsp, 0, CMDLEN_MAXBYTES);
|
||||
|
||||
// Min length is 1 byte so this should always be here
|
||||
switch (cmd[0]) {
|
||||
case FW_CMD_NAME_VERSION:
|
||||
puts("request: name-version\n");
|
||||
|
||||
if (hdr.len != 1) {
|
||||
// Bad length - give them an empty response
|
||||
fwreply(hdr, FW_RSP_NAME_VERSION, rsp);
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(rsp, (uint8_t *)&local_name0, 4);
|
||||
memcpy(rsp + 4, (uint8_t *)&local_name1, 4);
|
||||
memcpy(rsp + 8, (uint8_t *)&local_ver, 4);
|
||||
|
||||
fwreply(hdr, FW_RSP_NAME_VERSION, rsp);
|
||||
break;
|
||||
|
||||
case FW_CMD_LOAD_APP_SIZE:
|
||||
puts("request: load-app-size\n");
|
||||
|
||||
if (hdr.len != 32) {
|
||||
// Bad length
|
||||
rsp[0] = STATUS_BAD;
|
||||
fwreply(hdr, FW_RSP_LOAD_APP_SIZE, rsp);
|
||||
break;
|
||||
}
|
||||
|
||||
// cmd[1..4] contains the size.
|
||||
local_app_size = cmd[1] + (cmd[2] << 8) +
|
||||
(cmd[3] << 16) + (cmd[4] << 24);
|
||||
|
||||
puts("app size: ");
|
||||
putinthex(local_app_size);
|
||||
lf();
|
||||
|
||||
if (local_app_size > APP_MAX_SIZE) {
|
||||
rsp[0] = STATUS_BAD;
|
||||
fwreply(hdr, FW_RSP_LOAD_APP_SIZE, rsp);
|
||||
break;
|
||||
}
|
||||
|
||||
*app_size = local_app_size;
|
||||
*app_addr = 0;
|
||||
|
||||
// Reset where to start loading the program
|
||||
loadaddr = (uint8_t *)APP_RAM_ADDR;
|
||||
left = *app_size;
|
||||
|
||||
rsp[0] = STATUS_OK;
|
||||
fwreply(hdr, FW_RSP_LOAD_APP_SIZE, rsp);
|
||||
break;
|
||||
|
||||
case FW_CMD_LOAD_APP_DATA:
|
||||
puts("request: load-app-data\n");
|
||||
|
||||
if (hdr.len != 128 || *app_size == 0) {
|
||||
// Bad length of this command or bad app size -
|
||||
// they need to call FW_CMD_LOAD_APP_SIZE first
|
||||
rsp[0] = STATUS_BAD;
|
||||
fwreply(hdr, FW_RSP_LOAD_APP_DATA, rsp);
|
||||
break;
|
||||
}
|
||||
|
||||
if (left > 127) {
|
||||
nbytes = 127;
|
||||
} else {
|
||||
nbytes = left;
|
||||
}
|
||||
memcpy(loadaddr, cmd + 1, nbytes);
|
||||
loadaddr += nbytes;
|
||||
left -= nbytes;
|
||||
|
||||
if (left == 0) {
|
||||
uint8_t scratch[64];
|
||||
|
||||
puts("Fully loaded ");
|
||||
putinthex(*app_size);
|
||||
lf();
|
||||
|
||||
*app_addr = APP_RAM_ADDR;
|
||||
// Get the Blake2S digest of the app - store it
|
||||
// for later queries
|
||||
blake2s(digest, 32, NULL, 0,
|
||||
(const void *)*app_addr, *app_size);
|
||||
print_digest(digest);
|
||||
|
||||
// CDI = hash(uds, hash(app))
|
||||
uint32_t local_cdi[8];
|
||||
|
||||
// Only word aligned access to UDS
|
||||
wordcpy(scratch, (void *)uds, 8);
|
||||
memcpy(scratch + 32, digest, 32);
|
||||
blake2s((void *)local_cdi, 32, NULL, 0,
|
||||
(const void *)scratch, 64);
|
||||
// Only word aligned access to CDI
|
||||
wordcpy((void *)cdi, (void *)local_cdi, 8);
|
||||
}
|
||||
|
||||
rsp[0] = STATUS_OK;
|
||||
fwreply(hdr, FW_RSP_LOAD_APP_DATA, rsp);
|
||||
break;
|
||||
|
||||
case FW_CMD_RUN_APP:
|
||||
puts("request: run-app\n");
|
||||
|
||||
if (hdr.len != 1) {
|
||||
// Bad length
|
||||
rsp[0] = STATUS_BAD;
|
||||
fwreply(hdr, FW_RSP_RUN_APP, rsp);
|
||||
break;
|
||||
}
|
||||
|
||||
if (*app_size > 0 && *app_addr != 0) {
|
||||
rsp[0] = STATUS_OK;
|
||||
fwreply(hdr, FW_RSP_RUN_APP, rsp);
|
||||
|
||||
// Flip over to application mode
|
||||
*switch_app = 1;
|
||||
|
||||
// Jump to app - doesn't return
|
||||
// First clears memory of firmware remains
|
||||
puts("Jumping to ");
|
||||
putinthex(*app_addr);
|
||||
lf();
|
||||
// clang-format off
|
||||
asm volatile(
|
||||
"li a0, 0x40000000;" // MTA1_MKDF_RAM_BASE
|
||||
"li a1, 0x40010000;"
|
||||
"loop:;"
|
||||
"sw zero, 0(a0);"
|
||||
"addi a0, a0, 4;"
|
||||
"blt a0, a1, loop;"
|
||||
// Get value at MTA1_MKDF_MMIO_MTA1_APP_ADDR
|
||||
"lui a0,0xff000;"
|
||||
"lw a0,0x030(a0);"
|
||||
"jalr x0,0(a0);"
|
||||
::: "memory");
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
rsp[0] = STATUS_BAD;
|
||||
fwreply(hdr, FW_RSP_RUN_APP, rsp);
|
||||
break;
|
||||
|
||||
case FW_CMD_GET_APP_DIGEST:
|
||||
puts("request: get-app-digest\n");
|
||||
|
||||
memcpy(rsp, &digest, 32);
|
||||
fwreply(hdr, FW_RSP_GET_APP_DIGEST, rsp);
|
||||
break;
|
||||
|
||||
default:
|
||||
puts("Received unknown firmware command: 0x");
|
||||
puthex(cmd[0]);
|
||||
lf();
|
||||
}
|
||||
}
|
||||
|
||||
return (int)0xcafebabe;
|
||||
}
|
156
hw/application_fpga/fw/mta1_mkdf/proto.c
Normal file
156
hw/application_fpga/fw/mta1_mkdf/proto.c
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright (C) 2022 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "proto.h"
|
||||
#include "../mta1_mkdf_mem.h"
|
||||
#include "lib.h"
|
||||
#include "types.h"
|
||||
|
||||
// clang-format off
|
||||
static volatile uint32_t *can_rx = (volatile uint32_t *)MTA1_MKDF_MMIO_UART_RX_STATUS;
|
||||
static volatile uint32_t *rx = (volatile uint32_t *)MTA1_MKDF_MMIO_UART_RX_DATA;
|
||||
static volatile uint32_t *can_tx = (volatile uint32_t *)MTA1_MKDF_MMIO_UART_TX_STATUS;
|
||||
static volatile uint32_t *tx = (volatile uint32_t *)MTA1_MKDF_MMIO_UART_TX_DATA;
|
||||
static volatile uint32_t *led = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_LED;
|
||||
// clang-format on
|
||||
|
||||
uint8_t genhdr(uint8_t id, uint8_t endpoint, uint8_t status, enum cmdlen len)
|
||||
{
|
||||
return (id << 5) | (endpoint << 3) | (status << 2) | len;
|
||||
}
|
||||
|
||||
int parseframe(uint8_t b, struct frame_header *hdr)
|
||||
{
|
||||
if ((b & 0x80) != 0) {
|
||||
// Bad version
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((b & 0x4) != 0) {
|
||||
// Must be 0
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdr->id = (b & 0x60) >> 5;
|
||||
hdr->endpoint = (b & 0x18) >> 3;
|
||||
|
||||
// Length
|
||||
switch (b & 0x3) {
|
||||
case LEN_1:
|
||||
hdr->len = 1;
|
||||
break;
|
||||
case LEN_4:
|
||||
hdr->len = 4;
|
||||
break;
|
||||
case LEN_32:
|
||||
hdr->len = 32;
|
||||
break;
|
||||
case LEN_128:
|
||||
hdr->len = 128;
|
||||
break;
|
||||
default:
|
||||
// Unknown length
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Send a firmware reply with a frame header, response code rspcode and
|
||||
// following data in buf
|
||||
void fwreply(struct frame_header hdr, enum fwcmd rspcode, uint8_t *buf)
|
||||
{
|
||||
size_t nbytes;
|
||||
enum cmdlen len; // length covering (rspcode + length of buf)
|
||||
|
||||
switch (rspcode) {
|
||||
case FW_RSP_NAME_VERSION:
|
||||
len = LEN_32;
|
||||
nbytes = 32;
|
||||
break;
|
||||
|
||||
case FW_RSP_LOAD_APP_SIZE:
|
||||
len = LEN_4;
|
||||
nbytes = 4;
|
||||
break;
|
||||
|
||||
case FW_RSP_LOAD_APP_DATA:
|
||||
len = LEN_4;
|
||||
nbytes = 4;
|
||||
break;
|
||||
|
||||
case FW_RSP_RUN_APP:
|
||||
len = LEN_4;
|
||||
nbytes = 4;
|
||||
break;
|
||||
|
||||
case FW_RSP_GET_APP_DIGEST:
|
||||
len = LEN_128;
|
||||
nbytes = 128;
|
||||
break;
|
||||
|
||||
default:
|
||||
puts("fwreply(): Unknown response code: 0x");
|
||||
puthex(rspcode);
|
||||
lf();
|
||||
return;
|
||||
}
|
||||
|
||||
// Frame Protocol Header
|
||||
writebyte(genhdr(hdr.id, hdr.endpoint, 0x0, len));
|
||||
|
||||
// FW protocol header
|
||||
writebyte(rspcode);
|
||||
nbytes--;
|
||||
|
||||
write(buf, nbytes);
|
||||
}
|
||||
|
||||
void writebyte(uint8_t b)
|
||||
{
|
||||
for (;;) {
|
||||
if (*can_tx) {
|
||||
*tx = b;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write(uint8_t *buf, size_t nbytes)
|
||||
{
|
||||
for (int i = 0; i < nbytes; i++) {
|
||||
writebyte(buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t readbyte()
|
||||
{
|
||||
for (;;) {
|
||||
if (*can_rx) {
|
||||
return *rx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t readbyte_ledflash(int ledvalue, int loopcount)
|
||||
{
|
||||
int led_on = 0;
|
||||
for (;;) {
|
||||
*led = led_on ? ledvalue : 0;
|
||||
for (int i = 0; i < loopcount; i++) {
|
||||
if (*can_rx) {
|
||||
return *rx;
|
||||
}
|
||||
}
|
||||
led_on = !led_on;
|
||||
}
|
||||
}
|
||||
|
||||
void read(uint8_t *buf, size_t nbytes)
|
||||
{
|
||||
for (int n = 0; n < nbytes; n++) {
|
||||
buf[n] = readbyte();
|
||||
}
|
||||
}
|
62
hw/application_fpga/fw/mta1_mkdf/proto.h
Normal file
62
hw/application_fpga/fw/mta1_mkdf/proto.h
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2022 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#ifndef PROTO_H
|
||||
#define PROTO_H
|
||||
|
||||
enum endpoints {
|
||||
DST_HW_IFPGA,
|
||||
DST_HW_AFPGA,
|
||||
DST_FW,
|
||||
DST_SW
|
||||
};
|
||||
|
||||
enum cmdlen {
|
||||
LEN_1,
|
||||
LEN_4,
|
||||
LEN_32,
|
||||
LEN_128
|
||||
};
|
||||
|
||||
#define CMDLEN_MAXBYTES 128
|
||||
|
||||
// clang-format off
|
||||
enum fwcmd {
|
||||
FW_CMD_NAME_VERSION = 0x01,
|
||||
FW_RSP_NAME_VERSION = 0x02,
|
||||
FW_CMD_LOAD_APP_SIZE = 0x03,
|
||||
FW_RSP_LOAD_APP_SIZE = 0x04,
|
||||
FW_CMD_LOAD_APP_DATA = 0x05,
|
||||
FW_RSP_LOAD_APP_DATA = 0x06,
|
||||
FW_CMD_RUN_APP = 0x07,
|
||||
FW_RSP_RUN_APP = 0x08,
|
||||
FW_CMD_GET_APP_DIGEST = 0x09,
|
||||
FW_RSP_GET_APP_DIGEST = 0x10
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
enum status {
|
||||
STATUS_OK,
|
||||
STATUS_BAD
|
||||
};
|
||||
|
||||
struct frame_header {
|
||||
uint8_t id;
|
||||
enum endpoints endpoint;
|
||||
enum cmdlen len;
|
||||
};
|
||||
|
||||
uint8_t genhdr(uint8_t id, uint8_t endpoint, uint8_t status, enum cmdlen len);
|
||||
int parseframe(uint8_t b, struct frame_header *hdr);
|
||||
void fwreply(struct frame_header hdr, enum fwcmd rspcode, uint8_t *buf);
|
||||
void writebyte(uint8_t b);
|
||||
void write(uint8_t *buf, size_t nbytes);
|
||||
uint8_t readbyte();
|
||||
uint8_t readbyte_ledflash(int ledvalue, int loopcount);
|
||||
void read(uint8_t *buf, size_t nbytes);
|
||||
|
||||
#endif
|
72
hw/application_fpga/fw/mta1_mkdf/start.S
Normal file
72
hw/application_fpga/fw/mta1_mkdf/start.S
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* Copyright (C) 2022 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
.section ".text.init"
|
||||
.globl _start
|
||||
_start:
|
||||
li x1, 0
|
||||
li x2, 0
|
||||
li x3, 0
|
||||
li x4, 0
|
||||
li x5, 0
|
||||
li x6, 0
|
||||
li x7, 0
|
||||
li x8, 0
|
||||
li x9, 0
|
||||
li x10,0
|
||||
li x11,0
|
||||
li x12,0
|
||||
li x13,0
|
||||
li x14,0
|
||||
li x15,0
|
||||
li x16,0
|
||||
li x17,0
|
||||
li x18,0
|
||||
li x19,0
|
||||
li x20,0
|
||||
li x21,0
|
||||
li x22,0
|
||||
li x23,0
|
||||
li x24,0
|
||||
li x25,0
|
||||
li x26,0
|
||||
li x27,0
|
||||
li x28,0
|
||||
li x29,0
|
||||
li x30,0
|
||||
li x31,0
|
||||
|
||||
/* init stack to right under where we load app at 0x40010000 */
|
||||
li sp, 0x4000fff0
|
||||
|
||||
/* copy data section */
|
||||
la a0, _sidata
|
||||
la a1, _sdata
|
||||
la a2, _edata
|
||||
bge a1, a2, end_init_data
|
||||
|
||||
loop_init_data:
|
||||
lw a3, 0(a0)
|
||||
sw a3, 0(a1)
|
||||
addi a0, a0, 4
|
||||
addi a1, a1, 4
|
||||
blt a1, a2, loop_init_data
|
||||
|
||||
end_init_data:
|
||||
/* zero-init bss section */
|
||||
la a0, _sbss
|
||||
la a1, _ebss
|
||||
bge a0, a1, end_init_bss
|
||||
|
||||
loop_init_bss:
|
||||
sw zero, 0(a0)
|
||||
addi a0, a0, 4
|
||||
blt a0, a1, loop_init_bss
|
||||
|
||||
end_init_bss:
|
||||
call main
|
||||
|
||||
loop:
|
||||
j loop
|
19
hw/application_fpga/fw/mta1_mkdf/types.h
Normal file
19
hw/application_fpga/fw/mta1_mkdf/types.h
Normal file
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright (C) 2022 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef TYPES_H
|
||||
#define TYPES_H
|
||||
|
||||
typedef unsigned int uintptr_t;
|
||||
typedef unsigned long long uint64_t;
|
||||
typedef unsigned int uint32_t;
|
||||
typedef int int32_t;
|
||||
typedef long long int64_t;
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned long size_t;
|
||||
|
||||
#define NULL ((char *)0)
|
||||
|
||||
#endif
|
109
hw/application_fpga/fw/mta1_mkdf_mem.h
Normal file
109
hw/application_fpga/fw/mta1_mkdf_mem.h
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* QEMU RISC-V Board Compatible with Mullvad MTA1-MKDF platform
|
||||
*
|
||||
* Copyright (c) 2022 Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
// clang-format off
|
||||
|
||||
#ifndef HW_MTA1_MKDF_MEM_H
|
||||
#define HW_MTA1_MKDF_MEM_H
|
||||
|
||||
// The canonical location of this file is:
|
||||
// repo: https://github.com/tillitis/tillitis-key1
|
||||
// path: /hw/application_fpga/fw/mta1_mkdf_mem.h
|
||||
|
||||
// The contents are derived from the Verilog code. For use by QEMU model,
|
||||
// firmware, and apps.
|
||||
|
||||
enum {
|
||||
MTA1_MKDF_ROM_BASE = 0x00000000, // 0b00000000...
|
||||
MTA1_MKDF_RAM_BASE = 0x40000000, // 0b01000000...
|
||||
MTA1_MKDF_RESERVED_BASE = 0x80000000, // 0b10000000...
|
||||
MTA1_MKDF_MMIO_BASE = 0xc0000000, // 0b11000000...
|
||||
MTA1_MKDF_MMIO_SIZE = 0xffffffff - MTA1_MKDF_MMIO_BASE,
|
||||
|
||||
MTA1_MKDF_MMIO_TRNG_BASE = MTA1_MKDF_MMIO_BASE | 0x00000000,
|
||||
MTA1_MKDF_MMIO_TIMER_BASE = MTA1_MKDF_MMIO_BASE | 0x01000000,
|
||||
MTA1_MKDF_MMIO_UDS_BASE = MTA1_MKDF_MMIO_BASE | 0x02000000,
|
||||
MTA1_MKDF_MMIO_UART_BASE = MTA1_MKDF_MMIO_BASE | 0x03000000,
|
||||
MTA1_MKDF_MMIO_TOUCH_BASE = MTA1_MKDF_MMIO_BASE | 0x04000000,
|
||||
// This "core" only exists in QEMU
|
||||
MTA1_MKDF_MMIO_QEMU_BASE = MTA1_MKDF_MMIO_BASE | 0x3e000000,
|
||||
MTA1_MKDF_MMIO_MTA1_BASE = MTA1_MKDF_MMIO_BASE | 0x3f000000, // 0xff000000
|
||||
|
||||
MTA1_MKDF_NAME0_SUFFIX = 0x00,
|
||||
MTA1_MKDF_NAME1_SUFFIX = 0x04,
|
||||
MTA1_MKDF_VERSION_SUFFIX = 0x08,
|
||||
|
||||
MTA1_MKDF_MMIO_TRNG_NAME0 = MTA1_MKDF_MMIO_TRNG_BASE | MTA1_MKDF_NAME0_SUFFIX,
|
||||
MTA1_MKDF_MMIO_TRNG_NAME1 = MTA1_MKDF_MMIO_TRNG_BASE | MTA1_MKDF_NAME1_SUFFIX,
|
||||
MTA1_MKDF_MMIO_TRNG_VERSION = MTA1_MKDF_MMIO_TRNG_BASE | MTA1_MKDF_VERSION_SUFFIX,
|
||||
MTA1_MKDF_MMIO_TRNG_STATUS = MTA1_MKDF_MMIO_TRNG_BASE | 0x24,
|
||||
MTA1_MKDF_MMIO_TRNG_STATUS_READY_BIT = 0,
|
||||
MTA1_MKDF_MMIO_TRNG_SAMPLE_RATE = MTA1_MKDF_MMIO_TRNG_BASE | 0x40,
|
||||
MTA1_MKDF_MMIO_TRNG_ENTROPY = MTA1_MKDF_MMIO_TRNG_BASE | 0x80,
|
||||
|
||||
MTA1_MKDF_MMIO_TIMER_NAME0 = MTA1_MKDF_MMIO_TIMER_BASE | MTA1_MKDF_NAME0_SUFFIX,
|
||||
MTA1_MKDF_MMIO_TIMER_NAME1 = MTA1_MKDF_MMIO_TIMER_BASE | MTA1_MKDF_NAME1_SUFFIX,
|
||||
MTA1_MKDF_MMIO_TIMER_VERSION = MTA1_MKDF_MMIO_TIMER_BASE | MTA1_MKDF_VERSION_SUFFIX,
|
||||
MTA1_MKDF_MMIO_TIMER_CTRL = MTA1_MKDF_MMIO_TIMER_BASE | 0x20,
|
||||
MTA1_MKDF_MMIO_TIMER_CTRL_START_BIT = 0,
|
||||
MTA1_MKDF_MMIO_TIMER_CTRL_STOP_BIT = 1,
|
||||
MTA1_MKDF_MMIO_TIMER_STATUS = MTA1_MKDF_MMIO_TIMER_BASE | 0x24,
|
||||
MTA1_MKDF_MMIO_TIMER_STATUS_READY_BIT = 0,
|
||||
MTA1_MKDF_MMIO_TIMER_PRESCALER = MTA1_MKDF_MMIO_TIMER_BASE | 0x28,
|
||||
MTA1_MKDF_MMIO_TIMER_TIMER = MTA1_MKDF_MMIO_TIMER_BASE | 0x2c,
|
||||
|
||||
MTA1_MKDF_MMIO_UDS_NAME0 = MTA1_MKDF_MMIO_UDS_BASE | MTA1_MKDF_NAME0_SUFFIX,
|
||||
MTA1_MKDF_MMIO_UDS_NAME1 = MTA1_MKDF_MMIO_UDS_BASE | MTA1_MKDF_NAME1_SUFFIX,
|
||||
MTA1_MKDF_MMIO_UDS_VERSION = MTA1_MKDF_MMIO_UDS_BASE | MTA1_MKDF_VERSION_SUFFIX,
|
||||
MTA1_MKDF_MMIO_UDS_FIRST = MTA1_MKDF_MMIO_UDS_BASE | 0x40,
|
||||
MTA1_MKDF_MMIO_UDS_LAST = MTA1_MKDF_MMIO_UDS_BASE | 0x5c, // Address of last 32-bit word of UDS
|
||||
|
||||
MTA1_MKDF_MMIO_UART_NAME0 = MTA1_MKDF_MMIO_UART_BASE | MTA1_MKDF_NAME0_SUFFIX,
|
||||
MTA1_MKDF_MMIO_UART_NAME1 = MTA1_MKDF_MMIO_UART_BASE | MTA1_MKDF_NAME1_SUFFIX,
|
||||
MTA1_MKDF_MMIO_UART_VERSION = MTA1_MKDF_MMIO_UART_BASE | MTA1_MKDF_VERSION_SUFFIX,
|
||||
MTA1_MKDF_MMIO_UART_BIT_RATE = MTA1_MKDF_MMIO_UART_BASE | 0x40,
|
||||
MTA1_MKDF_MMIO_UART_DATA_BITS = MTA1_MKDF_MMIO_UART_BASE | 0x44,
|
||||
MTA1_MKDF_MMIO_UART_STOP_BITS = MTA1_MKDF_MMIO_UART_BASE | 0x48,
|
||||
MTA1_MKDF_MMIO_UART_RX_STATUS = MTA1_MKDF_MMIO_UART_BASE | 0x80,
|
||||
MTA1_MKDF_MMIO_UART_RX_DATA = MTA1_MKDF_MMIO_UART_BASE | 0x84,
|
||||
MTA1_MKDF_MMIO_UART_TX_STATUS = MTA1_MKDF_MMIO_UART_BASE | 0x100,
|
||||
MTA1_MKDF_MMIO_UART_TX_DATA = MTA1_MKDF_MMIO_UART_BASE | 0x104,
|
||||
|
||||
MTA1_MKDF_MMIO_TOUCH_NAME0 = MTA1_MKDF_MMIO_TOUCH_BASE | MTA1_MKDF_NAME0_SUFFIX,
|
||||
MTA1_MKDF_MMIO_TOUCH_NAME1 = MTA1_MKDF_MMIO_TOUCH_BASE | MTA1_MKDF_NAME1_SUFFIX,
|
||||
MTA1_MKDF_MMIO_TOUCH_VERSION = MTA1_MKDF_MMIO_TOUCH_BASE | MTA1_MKDF_VERSION_SUFFIX,
|
||||
MTA1_MKDF_MMIO_TOUCH_STATUS = MTA1_MKDF_MMIO_TOUCH_BASE | 0x24,
|
||||
MTA1_MKDF_MMIO_TOUCH_STATUS_EVENT_BIT = 0,
|
||||
|
||||
// TODO HW core/addr is not yet defined for this:
|
||||
MTA1_MKDF_MMIO_QEMU_UDA = MTA1_MKDF_MMIO_QEMU_BASE | 0x20,
|
||||
// This will only ever exist in QEMU:
|
||||
MTA1_MKDF_MMIO_QEMU_DEBUG = MTA1_MKDF_MMIO_QEMU_BASE | 0x1000,
|
||||
|
||||
MTA1_MKDF_MMIO_MTA1_NAME0 = MTA1_MKDF_MMIO_MTA1_BASE | MTA1_MKDF_NAME0_SUFFIX,
|
||||
MTA1_MKDF_MMIO_MTA1_NAME1 = MTA1_MKDF_MMIO_MTA1_BASE | MTA1_MKDF_NAME1_SUFFIX,
|
||||
MTA1_MKDF_MMIO_MTA1_VERSION = MTA1_MKDF_MMIO_MTA1_BASE | MTA1_MKDF_VERSION_SUFFIX,
|
||||
MTA1_MKDF_MMIO_MTA1_SWITCH_APP = MTA1_MKDF_MMIO_MTA1_BASE | 0x20,
|
||||
MTA1_MKDF_MMIO_MTA1_LED = MTA1_MKDF_MMIO_MTA1_BASE | 0x24,
|
||||
MTA1_MKDF_MMIO_MTA1_LED_R_BIT = 2,
|
||||
MTA1_MKDF_MMIO_MTA1_LED_G_BIT = 1,
|
||||
MTA1_MKDF_MMIO_MTA1_LED_B_BIT = 0,
|
||||
MTA1_MKDF_MMIO_MTA1_GPIO = MTA1_MKDF_MMIO_MTA1_BASE | 0x28,
|
||||
MTA1_MKDF_MMIO_MTA1_GPIO1_BIT = 0,
|
||||
MTA1_MKDF_MMIO_MTA1_GPIO2_BIT = 1,
|
||||
MTA1_MKDF_MMIO_MTA1_GPIO3_BIT = 2,
|
||||
MTA1_MKDF_MMIO_MTA1_GPIO4_BIT = 3,
|
||||
MTA1_MKDF_MMIO_MTA1_APP_ADDR = MTA1_MKDF_MMIO_MTA1_BASE | 0x30, // 0x4000_0000
|
||||
MTA1_MKDF_MMIO_MTA1_APP_SIZE = MTA1_MKDF_MMIO_MTA1_BASE | 0x34,
|
||||
MTA1_MKDF_MMIO_MTA1_DEBUG = MTA1_MKDF_MMIO_MTA1_BASE | 0x40,
|
||||
MTA1_MKDF_MMIO_MTA1_CDI_FIRST = MTA1_MKDF_MMIO_MTA1_BASE | 0x80,
|
||||
MTA1_MKDF_MMIO_MTA1_CDI_LAST = MTA1_MKDF_MMIO_MTA1_BASE | 0x9c, // Address of last 32-bit word of CDI.
|
||||
MTA1_MKDF_MMIO_MTA1_UDI_FIRST = MTA1_MKDF_MMIO_MTA1_BASE | 0xc0,
|
||||
MTA1_MKDF_MMIO_MTA1_UDI_LAST = MTA1_MKDF_MMIO_MTA1_BASE | 0xc4, // Address of last 32-bit word of UDI.
|
||||
};
|
||||
|
||||
#endif
|
4
hw/application_fpga/fw/testfw/Makefile
Normal file
4
hw/application_fpga/fw/testfw/Makefile
Normal file
|
@ -0,0 +1,4 @@
|
|||
.PHONY: fmt
|
||||
fmt:
|
||||
# Uses ../.clang-format
|
||||
clang-format --verbose -i main.c
|
171
hw/application_fpga/fw/testfw/main.c
Normal file
171
hw/application_fpga/fw/testfw/main.c
Normal file
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright (C) 2022 - Tillitis AB
|
||||
* SPDX-License-Identifier: GPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "../mta1_mkdf/lib.h"
|
||||
#include "../mta1_mkdf/proto.h"
|
||||
#include "../mta1_mkdf/types.h"
|
||||
#include "../mta1_mkdf_mem.h"
|
||||
|
||||
// clang-format off
|
||||
volatile uint32_t *mta1name0 = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_NAME0;
|
||||
volatile uint32_t *mta1name1 = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_NAME1;
|
||||
volatile uint32_t *uds = (volatile uint32_t *)MTA1_MKDF_MMIO_UDS_FIRST;
|
||||
volatile uint32_t *uda = (volatile uint32_t *)MTA1_MKDF_MMIO_QEMU_UDA; // Only in QEMU right now
|
||||
volatile uint32_t *cdi = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_CDI_FIRST;
|
||||
volatile uint32_t *udi = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_UDI_FIRST;
|
||||
volatile uint32_t *switch_app = (volatile uint32_t *)MTA1_MKDF_MMIO_MTA1_SWITCH_APP;
|
||||
// clang-format on
|
||||
|
||||
// TODO Real UDA is 4 words (16 bytes)
|
||||
#define UDA_WORDS 1
|
||||
|
||||
void test_puts(char *reason)
|
||||
{
|
||||
for (char *c = reason; *c != '\0'; c++) {
|
||||
writebyte(*c);
|
||||
}
|
||||
}
|
||||
|
||||
void test_putsn(char *p, int n)
|
||||
{
|
||||
for (int i = 0; i < n; i++) {
|
||||
writebyte(p[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void test_puthex(uint8_t c)
|
||||
{
|
||||
unsigned int upper = (c >> 4) & 0xf;
|
||||
unsigned int lower = c & 0xf;
|
||||
writebyte(upper < 10 ? '0' + upper : 'a' - 10 + upper);
|
||||
writebyte(lower < 10 ? '0' + lower : 'a' - 10 + lower);
|
||||
}
|
||||
|
||||
void test_puthexn(uint8_t *p, int n)
|
||||
{
|
||||
for (int i = 0; i < n; i++) {
|
||||
test_puthex(p[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void test_reverseword(uint32_t *wordp)
|
||||
{
|
||||
*wordp = ((*wordp & 0xff000000) >> 24) | ((*wordp & 0x00ff0000) >> 8) |
|
||||
((*wordp & 0x0000ff00) << 8) | ((*wordp & 0x000000ff) << 24);
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
uint8_t in;
|
||||
|
||||
// Wait for terminal program and a character to be typed
|
||||
in = readbyte();
|
||||
|
||||
test_puts("Hello, I'm testfw on:");
|
||||
// Output the MTA1 core's NAME0 and NAME1
|
||||
uint32_t name;
|
||||
wordcpy(&name, (void *)mta1name0, 1);
|
||||
test_reverseword(&name);
|
||||
test_putsn((char *)&name, 4);
|
||||
test_puts(" ");
|
||||
wordcpy(&name, (void *)mta1name1, 1);
|
||||
test_reverseword(&name);
|
||||
test_putsn((char *)&name, 4);
|
||||
test_puts("\r\n");
|
||||
|
||||
int anyfailed = 0;
|
||||
|
||||
uint32_t uds_local[8];
|
||||
uint32_t uds_zeros[8];
|
||||
memset(uds_zeros, 0, 8 * 4);
|
||||
// Should get non-empty UDS
|
||||
wordcpy(uds_local, (void *)uds, 8);
|
||||
if (memeq(uds_local, uds_zeros, 8 * 4)) {
|
||||
test_puts("FAIL: UDS empty!\r\n");
|
||||
anyfailed = 1;
|
||||
}
|
||||
// Should NOT be able to read from UDS again
|
||||
wordcpy(uds_local, (void *)uds, 8);
|
||||
if (!memeq(uds_local, uds_zeros, 8 * 4)) {
|
||||
test_puts("FAIL: Could read UDS a second time!\r\n");
|
||||
anyfailed = 1;
|
||||
}
|
||||
|
||||
// TODO test UDA once we have it in real hw
|
||||
// uint32_t uda_local[UDA_WORDS];
|
||||
// uint32_t uda_zeros[UDA_WORDS];
|
||||
// memset(uda_zeros, 0, UDA_WORDS*4);
|
||||
// // Should get non-empty UDA
|
||||
// wordcpy(uda_local, (void *)uda, UDA_WORDS);
|
||||
// if (memeq(uda_local, uda_zeros, UDA_WORDS*4)) {
|
||||
// test_puts("FAIL: UDA empty!\r\n");
|
||||
// anyfailed = 1;
|
||||
// }
|
||||
|
||||
uint32_t udi_local[2];
|
||||
uint32_t udi_zeros[2];
|
||||
memset(udi_zeros, 0, 2 * 4);
|
||||
// Should get non-empty UDI
|
||||
wordcpy(udi_local, (void *)udi, 2);
|
||||
if (memeq(udi_local, udi_zeros, 2 * 4)) {
|
||||
test_puts("FAIL: UDI empty!\r\n");
|
||||
anyfailed = 1;
|
||||
}
|
||||
|
||||
// Should be able to write to CDI in non-app mode.
|
||||
uint32_t cdi_writetest[8] = {0xdeafbeef, 0xdeafbeef, 0xdeafbeef,
|
||||
0xdeafbeef, 0xdeafbeef, 0xdeafbeef,
|
||||
0xdeafbeef, 0xdeafbeef};
|
||||
uint32_t cdi_readback[8];
|
||||
wordcpy((void *)cdi, cdi_writetest, 8);
|
||||
wordcpy(cdi_readback, (void *)cdi, 8);
|
||||
if (!memeq(cdi_writetest, cdi_readback, 8 * 4)) {
|
||||
test_puts("FAIL: Could not write to CDI in non-app mode!\r\n");
|
||||
anyfailed = 1;
|
||||
}
|
||||
|
||||
// Turn on application mode
|
||||
*switch_app = 1;
|
||||
|
||||
// Should NOT be able to read from UDS in app-mode.
|
||||
wordcpy(uds_local, (void *)uds, 8);
|
||||
if (!memeq(uds_local, uds_zeros, 8 * 4)) {
|
||||
test_puts("FAIL: Could read from UDS in app-mode!\r\n");
|
||||
anyfailed = 1;
|
||||
}
|
||||
|
||||
// TODO test UDA once we have in in real hw
|
||||
// // Now we should NOT be able to read from UDA.
|
||||
// wordcpy(uda_local, (void *)uda, UDA_WORDS);
|
||||
// if (!memeq(uda_local, uda_zeros, UDA_WORDS*4)) {
|
||||
// test_puts("FAIL: Could read from UDA in app-mode!\r\n");
|
||||
// anyfailed = 1;
|
||||
// }
|
||||
|
||||
uint32_t cdi_local[8];
|
||||
uint32_t cdi_local2[8];
|
||||
uint32_t cdi_zeros[8];
|
||||
memset(cdi_zeros, 0, 8 * 4);
|
||||
wordcpy(cdi_local, (void *)cdi, 8);
|
||||
// Write to CDI should NOT have any effect in app mode.
|
||||
wordcpy((void *)cdi, cdi_zeros, 8);
|
||||
wordcpy(cdi_local2, (void *)cdi, 8);
|
||||
if (!memeq(cdi_local, cdi_local2, 8 * 4)) {
|
||||
test_puts("FAIL: Could write to CDI in app-mode!\r\n");
|
||||
anyfailed = 1;
|
||||
}
|
||||
|
||||
if (anyfailed) {
|
||||
test_puts("Some test failed!\r\n");
|
||||
} else {
|
||||
test_puts("All tests passed.\r\n");
|
||||
}
|
||||
|
||||
test_puts("Now echoing what you type...\r\n");
|
||||
for (;;) {
|
||||
in = readbyte(); // blocks
|
||||
writebyte(in);
|
||||
}
|
||||
}
|
467
hw/application_fpga/rtl/application_fpga.v
Normal file
467
hw/application_fpga/rtl/application_fpga.v
Normal file
|
@ -0,0 +1,467 @@
|
|||
//======================================================================
|
||||
//
|
||||
// application_fpga.v
|
||||
// ------------------
|
||||
// Top level module of the application FPGA.
|
||||
// The design exposes a UART interface to allow a host to
|
||||
// send commands and receive resposes as needed load, execute and
|
||||
// communicate with applications.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module application_fpga(
|
||||
output wire interface_rx,
|
||||
input wire interface_tx,
|
||||
|
||||
input wire touch_event,
|
||||
|
||||
input wire app_gpio1,
|
||||
input wire app_gpio2,
|
||||
output wire app_gpio3,
|
||||
output wire app_gpio4,
|
||||
|
||||
output wire led_r,
|
||||
output wire led_g,
|
||||
output wire led_b
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Local parameters
|
||||
//----------------------------------------------------------------
|
||||
// Top level mem area prefixes.
|
||||
localparam ROM_PREFIX = 2'h0;
|
||||
localparam RAM_PREFIX = 2'h1;
|
||||
localparam RESERVED_PREFIX = 2'h2;
|
||||
localparam MMIO_PREFIX = 2'h3;
|
||||
|
||||
// MMIO core sub-prefixes.
|
||||
localparam TRNG_PREFIX = 6'h00;
|
||||
localparam TIMER_PREFIX = 6'h01;
|
||||
localparam UDS_PREFIX = 6'h02;
|
||||
localparam UART_PREFIX = 6'h03;
|
||||
localparam TOUCH_SENSE_PREFIX = 6'h04;
|
||||
localparam MTA1_PREFIX = 6'h3f;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Registers, memories with associated wires.
|
||||
//----------------------------------------------------------------
|
||||
reg [31 : 0] muxed_rdata_reg;
|
||||
reg [31 : 0] muxed_rdata_new;
|
||||
|
||||
reg muxed_ready_reg;
|
||||
reg muxed_ready_new;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Wires.
|
||||
//----------------------------------------------------------------
|
||||
wire clk;
|
||||
wire reset_n;
|
||||
|
||||
wire cpu_valid;
|
||||
wire [03 : 0] cpu_wstrb;
|
||||
wire [31 : 0] cpu_addr;
|
||||
wire [31 : 0] cpu_wdata;
|
||||
|
||||
/* verilator lint_off UNOPTFLAT */
|
||||
reg rom_cs;
|
||||
/* verilator lint_on UNOPTFLAT */
|
||||
reg [11 : 0] rom_address;
|
||||
wire [31 : 0] rom_read_data;
|
||||
wire rom_ready;
|
||||
|
||||
reg ram_cs;
|
||||
reg [3 : 0] ram_we;
|
||||
reg [14 : 0] ram_address;
|
||||
reg [31 : 0] ram_write_data;
|
||||
wire [31 : 0] ram_read_data;
|
||||
wire ram_ready;
|
||||
|
||||
/* verilator lint_off UNOPTFLAT */
|
||||
reg trng_cs;
|
||||
/* verilator lint_on UNOPTFLAT */
|
||||
reg trng_we;
|
||||
reg [7 : 0] trng_address;
|
||||
reg [31 : 0] trng_write_data;
|
||||
wire [31 : 0] trng_read_data;
|
||||
wire trng_ready;
|
||||
|
||||
/* verilator lint_off UNOPTFLAT */
|
||||
reg timer_cs;
|
||||
/* verilator lint_on UNOPTFLAT */
|
||||
reg timer_we;
|
||||
reg [7 : 0] timer_address;
|
||||
reg [31 : 0] timer_write_data;
|
||||
wire [31 : 0] timer_read_data;
|
||||
wire timer_ready;
|
||||
|
||||
/* verilator lint_off UNOPTFLAT */
|
||||
reg uds_cs;
|
||||
/* verilator lint_on UNOPTFLAT */
|
||||
reg [7 : 0] uds_address;
|
||||
wire [31 : 0] uds_read_data;
|
||||
wire uds_ready;
|
||||
|
||||
/* verilator lint_off UNOPTFLAT */
|
||||
reg uart_cs;
|
||||
/* verilator lint_on UNOPTFLAT */
|
||||
reg uart_we;
|
||||
reg [7 : 0] uart_address;
|
||||
reg [31 : 0] uart_write_data;
|
||||
wire [31 : 0] uart_read_data;
|
||||
wire uart_ready;
|
||||
|
||||
/* verilator lint_off UNOPTFLAT */
|
||||
reg touch_sense_cs;
|
||||
/* verilator lint_on UNOPTFLAT */
|
||||
reg touch_sense_we;
|
||||
reg [7 : 0] touch_sense_address;
|
||||
wire [31 : 0] touch_sense_read_data;
|
||||
wire touch_sense_ready;
|
||||
|
||||
/* verilator lint_off UNOPTFLAT */
|
||||
reg mta1_cs;
|
||||
/* verilator lint_on UNOPTFLAT */
|
||||
reg mta1_we;
|
||||
reg [7 : 0] mta1_address;
|
||||
reg [31 : 0] mta1_write_data;
|
||||
wire [31 : 0] mta1_read_data;
|
||||
wire mta1_ready;
|
||||
wire fw_app_mode;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Concurrent assignments.
|
||||
//----------------------------------------------------------------
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Module instantiations.
|
||||
//----------------------------------------------------------------
|
||||
// Use the FPGA internal High Frequency OSCillator as clock source.
|
||||
// 00: 48MHz, 01: 24MHz, 10: 12MHz, 11: 6MHz
|
||||
SB_HFOSC #(.CLKHF_DIV("0b10")
|
||||
) u_hfosc (.CLKHFPU(1'b1),.CLKHFEN(1'b1),.CLKHF(clk));
|
||||
|
||||
|
||||
reset_gen #(.RESET_CYCLES(200))
|
||||
reset_gen_inst(.clk(clk), .rst_n(reset_n));
|
||||
|
||||
|
||||
picorv32 #(
|
||||
.ENABLE_COUNTERS(0),
|
||||
.LATCHED_MEM_RDATA(0),
|
||||
.TWO_STAGE_SHIFT(0),
|
||||
.TWO_CYCLE_ALU(0),
|
||||
.CATCH_MISALIGN(0),
|
||||
.CATCH_ILLINSN(0),
|
||||
.COMPRESSED_ISA(1),
|
||||
.ENABLE_FAST_MUL(1),
|
||||
.ENABLE_DIV(0),
|
||||
.BARREL_SHIFTER(1)
|
||||
) cpu(
|
||||
.clk(clk),
|
||||
.resetn(reset_n),
|
||||
|
||||
.mem_valid(cpu_valid),
|
||||
.mem_ready(muxed_ready_reg),
|
||||
.mem_addr (cpu_addr),
|
||||
.mem_wdata(cpu_wdata),
|
||||
.mem_wstrb(cpu_wstrb),
|
||||
.mem_rdata(muxed_rdata_reg),
|
||||
|
||||
// Defined unsed ports. Makes lint happy,
|
||||
// but still needs to help lint with empty ports.
|
||||
/* verilator lint_off PINCONNECTEMPTY */
|
||||
.irq(32'h0),
|
||||
.eoi(),
|
||||
.trap(),
|
||||
.trace_valid(),
|
||||
.trace_data(),
|
||||
.mem_instr(),
|
||||
.mem_la_read(),
|
||||
.mem_la_write(),
|
||||
.mem_la_addr(),
|
||||
.mem_la_wdata(),
|
||||
.mem_la_wstrb(),
|
||||
.pcpi_valid(),
|
||||
.pcpi_insn(),
|
||||
.pcpi_rs1(),
|
||||
.pcpi_rs2(),
|
||||
.pcpi_wr(1'h0),
|
||||
.pcpi_rd(32'h0),
|
||||
.pcpi_wait(1'h0),
|
||||
.pcpi_ready(1'h0)
|
||||
/* verilator lint_on PINCONNECTEMPTY */
|
||||
);
|
||||
|
||||
|
||||
rom rom_inst(
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.cs(rom_cs),
|
||||
.address(rom_address),
|
||||
.read_data(rom_read_data),
|
||||
.ready(rom_ready)
|
||||
);
|
||||
|
||||
|
||||
ram ram_inst(
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.cs(ram_cs),
|
||||
.we(ram_we),
|
||||
.address(ram_address),
|
||||
.write_data(ram_write_data),
|
||||
.read_data(ram_read_data),
|
||||
.ready(ram_ready)
|
||||
);
|
||||
|
||||
|
||||
figaro trng_inst(
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
.cs(trng_cs),
|
||||
.we(trng_we),
|
||||
.address(trng_address),
|
||||
.write_data(trng_write_data),
|
||||
.read_data(trng_read_data),
|
||||
.ready(trng_ready)
|
||||
);
|
||||
|
||||
|
||||
timer timer_inst(
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.cs(timer_cs),
|
||||
.we(timer_we),
|
||||
.address(timer_address),
|
||||
.write_data(timer_write_data),
|
||||
.read_data(timer_read_data),
|
||||
.ready(timer_ready)
|
||||
);
|
||||
|
||||
|
||||
uds uds_inst(
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.fw_app_mode(fw_app_mode),
|
||||
|
||||
.cs(uds_cs),
|
||||
.address(uds_address),
|
||||
.read_data(uds_read_data),
|
||||
.ready(uds_ready)
|
||||
);
|
||||
|
||||
|
||||
uart uart_inst(
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.rxd(interface_tx),
|
||||
.txd(interface_rx),
|
||||
|
||||
.cs(uart_cs),
|
||||
.we(uart_we),
|
||||
.address(uart_address),
|
||||
.write_data(uart_write_data),
|
||||
.read_data(uart_read_data),
|
||||
.ready(uart_ready)
|
||||
);
|
||||
|
||||
|
||||
touch_sense touch_sense_inst(
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.touch_event(touch_event),
|
||||
|
||||
.cs(touch_sense_cs),
|
||||
.we(touch_sense_we),
|
||||
.address(touch_sense_address),
|
||||
.read_data(touch_sense_read_data),
|
||||
.ready(touch_sense_ready)
|
||||
);
|
||||
|
||||
|
||||
mta1 mta1_inst(
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.fw_app_mode(fw_app_mode),
|
||||
|
||||
.led_r(led_r),
|
||||
.led_g(led_g),
|
||||
.led_b(led_b),
|
||||
|
||||
.gpio1(app_gpio1),
|
||||
.gpio2(app_gpio2),
|
||||
.gpio3(app_gpio3),
|
||||
.gpio4(app_gpio4),
|
||||
|
||||
.cs(mta1_cs),
|
||||
.we(mta1_we),
|
||||
.address(mta1_address),
|
||||
.write_data(mta1_write_data),
|
||||
.read_data(mta1_read_data),
|
||||
.ready(mta1_ready)
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Reg_update.
|
||||
// Posedge triggered with synchronous, active low reset.
|
||||
//----------------------------------------------------------------
|
||||
always @(posedge clk)
|
||||
begin : reg_update
|
||||
if (!reset_n) begin
|
||||
muxed_rdata_reg <= 32'h0;
|
||||
muxed_ready_reg <= 1'h0;
|
||||
end
|
||||
|
||||
else begin
|
||||
muxed_rdata_reg <= muxed_rdata_new;
|
||||
muxed_ready_reg <= muxed_ready_new;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// cpu_mem_ctrl
|
||||
// CPU memory decode and control logic.
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin : cpu_mem_ctrl
|
||||
reg [1 : 0] area_prefix;
|
||||
reg [5 : 0] core_prefix;
|
||||
|
||||
area_prefix = cpu_addr[31 : 30];
|
||||
core_prefix = cpu_addr[29 : 24];
|
||||
|
||||
muxed_ready_new = 1'h0;
|
||||
muxed_rdata_new = 32'h0;
|
||||
|
||||
rom_cs = 1'h0;
|
||||
rom_address = cpu_addr[13 : 2];
|
||||
|
||||
ram_cs = 1'h0;
|
||||
ram_we = cpu_wstrb;
|
||||
ram_address = cpu_addr[16 : 2];
|
||||
ram_write_data = cpu_wdata;
|
||||
|
||||
trng_cs = 1'h0;
|
||||
trng_we = |cpu_wstrb;
|
||||
trng_address = cpu_addr[10 : 2];
|
||||
trng_write_data = cpu_wdata;
|
||||
|
||||
timer_cs = 1'h0;
|
||||
timer_we = |cpu_wstrb;
|
||||
timer_address = cpu_addr[10 : 2];
|
||||
timer_write_data = cpu_wdata;
|
||||
|
||||
uds_cs = 1'h0;
|
||||
uds_address = cpu_addr[10 : 2];
|
||||
|
||||
uart_cs = 1'h0;
|
||||
uart_we = |cpu_wstrb;
|
||||
uart_address = cpu_addr[10 : 2];
|
||||
uart_write_data = cpu_wdata;
|
||||
|
||||
touch_sense_cs = 1'h0;
|
||||
touch_sense_we = |cpu_wstrb;
|
||||
touch_sense_address = cpu_addr[10 : 2];
|
||||
|
||||
mta1_cs = 1'h0;
|
||||
mta1_we = |cpu_wstrb;
|
||||
mta1_address = cpu_addr[10 : 2];
|
||||
mta1_write_data = cpu_wdata;
|
||||
|
||||
if (cpu_valid && !muxed_ready_reg) begin
|
||||
case (area_prefix)
|
||||
ROM_PREFIX: begin
|
||||
rom_cs = 1'h1;
|
||||
muxed_rdata_new = rom_read_data;
|
||||
muxed_ready_new = rom_ready;
|
||||
end
|
||||
|
||||
RAM_PREFIX: begin
|
||||
ram_cs = 1'h1;
|
||||
muxed_rdata_new = ram_read_data;
|
||||
muxed_ready_new = ram_ready;
|
||||
end
|
||||
|
||||
RESERVED_PREFIX: begin
|
||||
muxed_rdata_new = 32'h0;
|
||||
muxed_ready_new = 1'h1;
|
||||
end
|
||||
|
||||
MMIO_PREFIX: begin
|
||||
case (core_prefix)
|
||||
TRNG_PREFIX: begin
|
||||
trng_cs = 1'h1;
|
||||
muxed_rdata_new = trng_read_data;
|
||||
muxed_ready_new = trng_ready;
|
||||
end
|
||||
|
||||
TIMER_PREFIX: begin
|
||||
timer_cs = 1'h1;
|
||||
muxed_rdata_new = timer_read_data;
|
||||
muxed_ready_new = timer_ready;
|
||||
end
|
||||
|
||||
UDS_PREFIX: begin
|
||||
uds_cs = 1'h1;
|
||||
muxed_rdata_new = uds_read_data;
|
||||
muxed_ready_new = uds_ready;
|
||||
end
|
||||
|
||||
UART_PREFIX: begin
|
||||
uart_cs = 1'h1;
|
||||
muxed_rdata_new = uart_read_data;
|
||||
muxed_ready_new = uart_ready;
|
||||
end
|
||||
|
||||
TOUCH_SENSE_PREFIX: begin
|
||||
touch_sense_cs = 1'h1;
|
||||
muxed_rdata_new = touch_sense_read_data;
|
||||
muxed_ready_new = touch_sense_ready;
|
||||
end
|
||||
|
||||
MTA1_PREFIX: begin
|
||||
mta1_cs = 1'h1;
|
||||
muxed_rdata_new = mta1_read_data;
|
||||
muxed_ready_new = mta1_ready;
|
||||
end
|
||||
|
||||
default: begin
|
||||
muxed_rdata_new = 32'h0;
|
||||
muxed_ready_new = 1'h1;
|
||||
end
|
||||
endcase // case (core_prefix)
|
||||
end // case: MMIO_PREFIX
|
||||
|
||||
default: begin
|
||||
muxed_rdata_new = 32'h0;
|
||||
muxed_ready_new = 1'h1;
|
||||
end
|
||||
endcase // case (area_prefix)
|
||||
end
|
||||
end
|
||||
endmodule // application_fpga
|
||||
|
||||
//======================================================================
|
||||
// EOF application_fpga.v
|
||||
//======================================================================
|
144
hw/application_fpga/rtl/ram.v
Normal file
144
hw/application_fpga/rtl/ram.v
Normal file
|
@ -0,0 +1,144 @@
|
|||
//======================================================================
|
||||
//
|
||||
// ram.v
|
||||
// -----
|
||||
// Module that encapsulates the four SPRAM blocks in the Lattice
|
||||
// iCE40UP 5K device. This creates a single 32-bit wide,
|
||||
// 128 kByte large memory.
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module ram(
|
||||
input wire clk,
|
||||
input wire reset_n,
|
||||
input wire cs,
|
||||
input wire [03 : 0] we,
|
||||
input wire [14 : 0] address,
|
||||
input wire [31 : 0] write_data,
|
||||
output wire [31 : 0] read_data,
|
||||
output wire ready
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Registers and wires.
|
||||
//----------------------------------------------------------------
|
||||
reg ready_reg;
|
||||
|
||||
reg cs0;
|
||||
reg cs1;
|
||||
reg [31 : 0] read_data0;
|
||||
reg [31 : 0] read_data1;
|
||||
reg [31 : 0] muxed_read_data;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Concurrent assignment of ports.
|
||||
//----------------------------------------------------------------
|
||||
assign read_data = muxed_read_data;
|
||||
assign ready = ready_reg;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// SPRAM instances.
|
||||
//----------------------------------------------------------------
|
||||
SB_SPRAM256KA spram0(
|
||||
.ADDRESS(address[13:0]),
|
||||
.DATAIN(write_data[15:0]),
|
||||
.MASKWREN({we[1], we[1], we[0], we[0]}),
|
||||
.WREN(we[1] | we[0]),
|
||||
.CHIPSELECT(cs0),
|
||||
.CLOCK(clk),
|
||||
.STANDBY(1'b0),
|
||||
.SLEEP(1'b0),
|
||||
.POWEROFF(1'b1),
|
||||
.DATAOUT(read_data0[15:0])
|
||||
);
|
||||
|
||||
SB_SPRAM256KA spram1(
|
||||
.ADDRESS(address[13:0]),
|
||||
.DATAIN(write_data[31:16]),
|
||||
.MASKWREN({we[3], we[3], we[2], we[2]}),
|
||||
.WREN(we[3] | we[2]),
|
||||
.CHIPSELECT(cs0),
|
||||
.CLOCK(clk),
|
||||
.STANDBY(1'b0),
|
||||
.SLEEP(1'b0),
|
||||
.POWEROFF(1'b1),
|
||||
.DATAOUT(read_data0[31:16])
|
||||
);
|
||||
|
||||
|
||||
SB_SPRAM256KA spram2(
|
||||
.ADDRESS(address[13:0]),
|
||||
.DATAIN(write_data[15:0]),
|
||||
.MASKWREN({we[1], we[1], we[0], we[0]}),
|
||||
.WREN(we[1] | we[0]),
|
||||
.CHIPSELECT(cs1),
|
||||
.CLOCK(clk),
|
||||
.STANDBY(1'b0),
|
||||
.SLEEP(1'b0),
|
||||
.POWEROFF(1'b1),
|
||||
.DATAOUT(read_data1[15:0])
|
||||
);
|
||||
|
||||
SB_SPRAM256KA spram3(
|
||||
.ADDRESS(address[13:0]),
|
||||
.DATAIN(write_data[31:16]),
|
||||
.MASKWREN({we[3], we[3], we[2], we[2]}),
|
||||
.WREN(we[3] | we[2]),
|
||||
.CHIPSELECT(cs1),
|
||||
.CLOCK(clk),
|
||||
.STANDBY(1'b0),
|
||||
.SLEEP(1'b0),
|
||||
.POWEROFF(1'b1),
|
||||
.DATAOUT(read_data1[31:16])
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// reg_update
|
||||
//
|
||||
// Posedge triggered with synchronous, active low reset.
|
||||
// This simply creates a one cycle access latency to match
|
||||
// the latency of the spram blocks.
|
||||
//----------------------------------------------------------------
|
||||
always @(posedge clk)
|
||||
begin : reg_update
|
||||
if (!reset_n) begin
|
||||
ready_reg <= 1'h0;
|
||||
end
|
||||
else begin
|
||||
ready_reg <= cs;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// mem_mux
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin : mem_mux
|
||||
cs0 = 1'h0;
|
||||
cs1 = 1'h0;
|
||||
|
||||
if (address[14]) begin
|
||||
cs1 = cs;
|
||||
muxed_read_data = read_data1;
|
||||
end else begin
|
||||
cs0 = cs;
|
||||
muxed_read_data = read_data0;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule // ram
|
||||
|
||||
//======================================================================
|
||||
// EOF ram.v
|
||||
//======================================================================
|
72
hw/application_fpga/rtl/reset_gen.v
Normal file
72
hw/application_fpga/rtl/reset_gen.v
Normal file
|
@ -0,0 +1,72 @@
|
|||
//======================================================================
|
||||
//
|
||||
// reset_gen.v
|
||||
// -----------
|
||||
// Reset generator for iCE40 based systems.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module reset_gen #(parameter RESET_CYCLES = 200)
|
||||
(
|
||||
input wire clk,
|
||||
output wire rst_n
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Registers with associated wires.
|
||||
//----------------------------------------------------------------
|
||||
reg [7 : 0] rst_ctr_reg = 8'h0;
|
||||
reg [7 : 0] rst_ctr_new;
|
||||
reg rst_ctr_we;
|
||||
|
||||
reg rst_n_reg = 1'h0;
|
||||
reg rst_n_new;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Concurrent assignment.
|
||||
//----------------------------------------------------------------
|
||||
assign rst_n = rst_n_reg;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// reg_update.
|
||||
//----------------------------------------------------------------
|
||||
always @(posedge clk)
|
||||
begin : reg_update
|
||||
rst_n_reg <= rst_n_new;
|
||||
|
||||
if (rst_ctr_we)
|
||||
rst_ctr_reg <= rst_ctr_new;
|
||||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// rst_logic.
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin : rst_logic
|
||||
rst_n_new = 1'h1;
|
||||
rst_ctr_new = 8'h0;
|
||||
rst_ctr_we = 1'h0;
|
||||
|
||||
if (rst_ctr_reg < RESET_CYCLES) begin
|
||||
rst_n_new = 1'h0;
|
||||
rst_ctr_new = rst_ctr_reg + 1'h1;
|
||||
rst_ctr_we = 1'h1;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule // reset_gen
|
||||
|
||||
//======================================================================
|
||||
// EOF reset_gen.v
|
||||
//======================================================================
|
67
hw/application_fpga/rtl/rom.v
Normal file
67
hw/application_fpga/rtl/rom.v
Normal file
|
@ -0,0 +1,67 @@
|
|||
//======================================================================
|
||||
//
|
||||
// rom..v
|
||||
// ------
|
||||
// Firmware ROM module. Implemented using Embedded Block RAM
|
||||
// in the FPGA.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module rom(
|
||||
input wire clk,
|
||||
input wire reset_n,
|
||||
|
||||
input wire cs,
|
||||
input wire [11 : 0] address,
|
||||
output wire [31 : 0] read_data,
|
||||
output wire ready
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Registers, memories with associated wires.
|
||||
//----------------------------------------------------------------
|
||||
// Size of the sysMem Embedded Block RAM (EBR) memory primarily
|
||||
// used for code storage (ROM). The size is number of
|
||||
// 32-bit words. Each EBR is 4kbit in size, and (at most)
|
||||
// 16-bit wide. Thus means that we use pairs of EBRs, and
|
||||
// each pair store 256 32bit words.
|
||||
// The size of the EBR allocated to memory must match the
|
||||
// size of the firmware file generated by the Makefile.
|
||||
localparam EBR_MEM_SIZE = `BRAM_FW_SIZE;
|
||||
reg [31 : 0] memory [0 : (EBR_MEM_SIZE - 1)];
|
||||
initial $readmemh(`FIRMWARE_HEX, memory);
|
||||
|
||||
reg [31 : 0] rom_rdata;
|
||||
|
||||
reg rom_ready;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Concurrent assignments of ports.
|
||||
//----------------------------------------------------------------
|
||||
assign read_data = rom_rdata;
|
||||
assign ready = rom_ready;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// rom_logic
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin : rom_logic
|
||||
rom_rdata = memory[address];
|
||||
rom_ready = cs;
|
||||
end
|
||||
|
||||
endmodule // rom
|
||||
|
||||
//======================================================================
|
||||
// EOF rom..v
|
||||
//======================================================================
|
92
hw/application_fpga/rtl/spram.v
Normal file
92
hw/application_fpga/rtl/spram.v
Normal file
|
@ -0,0 +1,92 @@
|
|||
//======================================================================
|
||||
//
|
||||
// spram.v
|
||||
// -------
|
||||
// Module that encapsulates two of the SPRAM blocks in the Lattice
|
||||
// iCE40UP 5K device. This creates a single 32-bit wide,
|
||||
// 64 kByte large memory.
|
||||
//
|
||||
//
|
||||
// Author: Joachim Strombergson
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
`default_nettype none
|
||||
|
||||
module spram(
|
||||
input wire clk,
|
||||
input wire rst_n,
|
||||
input wire cs,
|
||||
input wire [03 : 0] wen,
|
||||
input wire [13 : 0] addr,
|
||||
input wire [31 : 0] wdata,
|
||||
output wire ready,
|
||||
output wire [31 : 0] rdata
|
||||
);
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Registers and wires.
|
||||
//----------------------------------------------------------------
|
||||
reg ready_reg;
|
||||
reg ready_new;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
//----------------------------------------------------------------
|
||||
assign ready = ready_reg;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// SPRAM instances.
|
||||
//----------------------------------------------------------------
|
||||
SB_SPRAM256KA spram0(
|
||||
.ADDRESS(addr[13:0]),
|
||||
.DATAIN(wdata[15:0]),
|
||||
.MASKWREN({wen[1], wen[1], wen[0], wen[0]}),
|
||||
.WREN(wen[1]|wen[0]),
|
||||
.CHIPSELECT(cs),
|
||||
.CLOCK(clk),
|
||||
.STANDBY(1'b0),
|
||||
.SLEEP(1'b0),
|
||||
.POWEROFF(1'b1),
|
||||
.DATAOUT(rdata[15:0])
|
||||
);
|
||||
|
||||
SB_SPRAM256KA spram1(
|
||||
.ADDRESS(addr[13:0]),
|
||||
.DATAIN(wdata[31:16]),
|
||||
.MASKWREN({wen[3], wen[3], wen[2], wen[2]}),
|
||||
.WREN(wen[3]|wen[2]),
|
||||
.CHIPSELECT(cs),
|
||||
.CLOCK(clk),
|
||||
.STANDBY(1'b0),
|
||||
.SLEEP(1'b0),
|
||||
.POWEROFF(1'b1),
|
||||
.DATAOUT(rdata[31:16])
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// reg_update.
|
||||
//
|
||||
// Posedge triggered with synchronous, active low reset.
|
||||
// This simply creates a one cycle access delay to allow the
|
||||
// memory access to complete.
|
||||
//----------------------------------------------------------------
|
||||
always @(posedge clk)
|
||||
begin : reg_update
|
||||
if (!rst_n) begin
|
||||
ready_reg <= 1'h0;
|
||||
end
|
||||
else begin
|
||||
ready_reg <= cs;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule // spram
|
||||
|
||||
//======================================================================
|
||||
// EOF spram.v
|
||||
//======================================================================
|
342
hw/application_fpga/tb/application_fpga_verilator.cc
Normal file
342
hw/application_fpga/tb/application_fpga_verilator.cc
Normal file
|
@ -0,0 +1,342 @@
|
|||
//======================================================================
|
||||
//
|
||||
// application_fpga_verilator.cc
|
||||
// -----------------------------
|
||||
// Wrapper to allow simulation of the application_fpga using Verilator.
|
||||
//
|
||||
//
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
#include <pty.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "Vapplication_fpga.h"
|
||||
#include "verilated.h"
|
||||
|
||||
// Joachim says:
|
||||
// Clock: 12 MHz, 38400 bps
|
||||
// Divisor = 12*10E6 / 38400 = 312
|
||||
#define BIT_DIV 312
|
||||
|
||||
struct uart {
|
||||
int bit_div;
|
||||
unsigned int ts;
|
||||
|
||||
unsigned int tx_next_ts;
|
||||
unsigned int rx_next_ts;
|
||||
|
||||
int rx_state; /* 0, (idle), 1 (start), 2..9 (data), 10 (stop), 11 (stop end) */
|
||||
int tx_state; /* 0, (idle), 1..8 (data), 9 (stop), 10 (stop end) */
|
||||
uint8_t tx_data;
|
||||
uint8_t rx_data;
|
||||
int rx_has_data;
|
||||
int tx_has_data;
|
||||
uint8_t *tx;
|
||||
uint8_t *rx;
|
||||
};
|
||||
|
||||
void uart_init(struct uart *u, uint8_t *tx, uint8_t *rx, int bit_div);
|
||||
void uart_tick(struct uart *u);
|
||||
int uart_can_send(struct uart *u);
|
||||
int uart_send(struct uart *u, uint8_t data);
|
||||
int uart_recv(struct uart *u, uint8_t *data);
|
||||
|
||||
void uart_init(struct uart *u, uint8_t *tx, uint8_t *rx, int bit_div)
|
||||
{
|
||||
u->bit_div = bit_div;
|
||||
u->ts = 0;
|
||||
u->tx_next_ts = 0;
|
||||
u->rx_next_ts = 0;
|
||||
u->rx_state = 0;
|
||||
u->tx_state = 0;
|
||||
u->tx_data = 0;
|
||||
u->rx_data = 0;
|
||||
u->rx_has_data = 0;
|
||||
u->tx_has_data = 0;
|
||||
u->tx = tx;
|
||||
*u->tx = 1;
|
||||
u->rx = rx;
|
||||
}
|
||||
|
||||
void uart_rx_tick(struct uart *u)
|
||||
{
|
||||
if (u->rx_state == 0) {
|
||||
// Idle
|
||||
if (!u->rx_has_data && !*u->rx) { // Active low
|
||||
u->rx_next_ts = u->ts + u->bit_div / 2; // sample mid-point
|
||||
u->rx_state = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (u->rx_state == 1) {
|
||||
// Start
|
||||
if (u->ts < u->rx_next_ts)
|
||||
return;
|
||||
|
||||
if (*u->rx) {
|
||||
u->rx_state = 0; // Back to idle, shouldn't happen
|
||||
return;
|
||||
}
|
||||
|
||||
u->rx_next_ts += u->bit_div;
|
||||
u->rx_data = 0;
|
||||
u->rx_state = 2;
|
||||
return;
|
||||
}
|
||||
|
||||
if (u->rx_state > 1 && u->rx_state < 10) {
|
||||
// Data
|
||||
if (u->ts < u->rx_next_ts)
|
||||
return;
|
||||
|
||||
u->rx_next_ts += u->bit_div;
|
||||
u->rx_data |= (!!*u->rx) << (u->rx_state - 2);
|
||||
u->rx_state++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (u->rx_state == 10) {
|
||||
// Stop
|
||||
if (u->ts < u->rx_next_ts)
|
||||
return;
|
||||
|
||||
if (!*u->rx) {
|
||||
u->rx_state = 0; // Back to dle, shouldn't happen
|
||||
return;
|
||||
}
|
||||
|
||||
u->rx_next_ts += u->bit_div/2;
|
||||
u->rx_has_data = 1;
|
||||
u->rx_state = 11;
|
||||
return;
|
||||
}
|
||||
|
||||
if (u->rx_state == 11) {
|
||||
if (u->ts < u->rx_next_ts)
|
||||
return;
|
||||
|
||||
u->rx_state = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int uart_recv(struct uart *u, uint8_t *data)
|
||||
{
|
||||
if (u->rx_has_data && (u->rx_state == 0 || u->rx_state == 11)) {
|
||||
*data = u->rx_data;
|
||||
u->rx_has_data = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void uart_tx_tick(struct uart *u)
|
||||
{
|
||||
if (u->tx_state == 0) {
|
||||
// Idle
|
||||
if (u->tx_has_data) {
|
||||
u->tx_next_ts = u->ts + u->bit_div;
|
||||
*u->tx = 0; // Start
|
||||
u->tx_state = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (u->tx_state > 0 && u->tx_state < 9) {
|
||||
// Data
|
||||
if (u->ts < u->tx_next_ts)
|
||||
return;
|
||||
|
||||
u->tx_next_ts += u->bit_div;
|
||||
*u->tx = (u->tx_data >> (u->tx_state - 1)) & 1;
|
||||
u->tx_state++;
|
||||
return;
|
||||
}
|
||||
|
||||
if (u->tx_state == 9) {
|
||||
// Stop
|
||||
if (u->ts < u->tx_next_ts)
|
||||
return;
|
||||
|
||||
u->tx_next_ts += u->bit_div;
|
||||
*u->tx = 1; // Stop
|
||||
u->tx_has_data = 0;
|
||||
u->tx_state = 10;
|
||||
return;
|
||||
}
|
||||
|
||||
if (u->tx_state == 10) {
|
||||
if (u->ts < u->tx_next_ts)
|
||||
return;
|
||||
|
||||
u->tx_state = 0;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int uart_can_send(struct uart *u)
|
||||
{
|
||||
return !u->tx_has_data;
|
||||
}
|
||||
|
||||
int uart_send(struct uart *u, uint8_t data)
|
||||
{
|
||||
if (!uart_can_send(u))
|
||||
return 0;
|
||||
|
||||
u->tx_has_data = 1;
|
||||
u->tx_data = data;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void uart_tick(struct uart *u)
|
||||
{
|
||||
u->ts++;
|
||||
|
||||
uart_rx_tick(u);
|
||||
uart_tx_tick(u);
|
||||
}
|
||||
|
||||
struct pty {
|
||||
int amaster;
|
||||
int aslave;
|
||||
char slave[32];
|
||||
};
|
||||
|
||||
int pty_init(struct pty *p);
|
||||
int pty_can_recv(struct pty *p);
|
||||
int pty_recv(struct pty *p, uint8_t *data);
|
||||
void pty_send(struct pty *p, uint8_t data);
|
||||
|
||||
int pty_init(struct pty *p)
|
||||
{
|
||||
struct termios tty;
|
||||
int flags;
|
||||
|
||||
memset(p, 0, sizeof(*p));
|
||||
|
||||
if (openpty(&p->amaster, &p->aslave, p->slave, NULL, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
if (tcgetattr(p->aslave, &tty) < 0)
|
||||
return -1;
|
||||
cfmakeraw(&tty);
|
||||
if (tcsetattr(p->aslave, TCSAFLUSH, &tty) < 0)
|
||||
return -1;
|
||||
|
||||
if ((flags = fcntl(p->amaster, F_GETFL, 0) < 0))
|
||||
return -1;
|
||||
|
||||
flags |= O_NONBLOCK;
|
||||
if (fcntl(p->amaster, F_SETFL, flags) < 0)
|
||||
return -1;
|
||||
|
||||
printf("pty: %s\n", p->slave);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pty_can_recv(struct pty *p)
|
||||
{
|
||||
struct pollfd fds = {p->amaster, POLLIN, 0};
|
||||
char c;
|
||||
|
||||
return poll(&fds, 1, 0) == 1;
|
||||
}
|
||||
|
||||
int pty_recv(struct pty *p, uint8_t *data)
|
||||
{
|
||||
return read(p->amaster, data, 1) == 1;
|
||||
}
|
||||
|
||||
void pty_send(struct pty *p, uint8_t data)
|
||||
{
|
||||
ssize_t i __attribute__((unused));
|
||||
|
||||
i = write(p->amaster, &data, 1);
|
||||
}
|
||||
|
||||
volatile int touch_cyc = 0;
|
||||
|
||||
void sighandler(int)
|
||||
{
|
||||
touch_cyc = 1000;
|
||||
}
|
||||
|
||||
void touch(uint8_t *touch_event)
|
||||
{
|
||||
if (touch_cyc > 0) {
|
||||
touch_cyc--;
|
||||
*touch_event = 1;
|
||||
} else {
|
||||
*touch_event = 0;
|
||||
}
|
||||
}
|
||||
|
||||
vluint64_t main_time = 0;
|
||||
double sc_time_stamp()
|
||||
{
|
||||
return main_time;
|
||||
}
|
||||
|
||||
int main(int argc, char **argv, char **env)
|
||||
{
|
||||
Verilated::commandArgs(argc, argv);
|
||||
int r = 0, g = 0, b = 0;
|
||||
Vapplication_fpga top;
|
||||
struct uart u;
|
||||
struct pty p;
|
||||
int err;
|
||||
|
||||
if (signal(SIGUSR1, sighandler) == SIG_ERR)
|
||||
return -1;
|
||||
printf("generate touch event: \"$ kill -USR1 %d\"\n", (int)getpid());
|
||||
|
||||
err = pty_init(&p);
|
||||
if (err)
|
||||
return -1;
|
||||
|
||||
uart_init(&u, &top.interface_tx, &top.interface_rx, BIT_DIV);
|
||||
|
||||
top.clk = 0;
|
||||
|
||||
while (!Verilated::gotFinish()) {
|
||||
uint8_t to_host = 0;
|
||||
|
||||
top.clk = !top.clk;
|
||||
|
||||
if (main_time < 10)
|
||||
goto skip;
|
||||
|
||||
if (!top.clk) {
|
||||
touch(&top.touch_event);
|
||||
uart_tick(&u);
|
||||
}
|
||||
|
||||
if (pty_can_recv(&p) && uart_can_send(&u)) {
|
||||
uint8_t from_host = 0;
|
||||
|
||||
pty_recv(&p, &from_host);
|
||||
uart_send(&u, from_host);
|
||||
}
|
||||
|
||||
if (uart_recv(&u, &to_host) == 1) {
|
||||
pty_send(&p, to_host);
|
||||
}
|
||||
skip:
|
||||
main_time++;
|
||||
top.eval();
|
||||
|
||||
}
|
||||
}
|
482
hw/application_fpga/tb/application_fpga_vsim.v
Normal file
482
hw/application_fpga/tb/application_fpga_vsim.v
Normal file
|
@ -0,0 +1,482 @@
|
|||
//======================================================================
|
||||
//
|
||||
// application_fpga.v
|
||||
// ------------------
|
||||
// Top level module of the application FPGA.
|
||||
// The design exposes a UART interface to allow a host to
|
||||
// send commands and receive resposes as needed load, execute and
|
||||
// communicate with applications.
|
||||
//
|
||||
//
|
||||
// Copyright (C) 2022 - Tillitis AB
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
//
|
||||
//======================================================================
|
||||
|
||||
`default_nettype none
|
||||
|
||||
//`define VERBOSE
|
||||
|
||||
`ifdef VERBOSE
|
||||
`define verbose(debug_command) debug_command
|
||||
`else
|
||||
`define verbose(debug_command)
|
||||
`endif
|
||||
|
||||
|
||||
module application_fpga(
|
||||
input wire clk,
|
||||
|
||||
output wire valid,
|
||||
output wire [03 : 0] wstrb,
|
||||
output wire [31 : 0] addr,
|
||||
output wire [31 : 0] wdata,
|
||||
output wire [31 : 0] rdata,
|
||||
output wire ready,
|
||||
|
||||
output wire interface_rx,
|
||||
input wire interface_tx,
|
||||
|
||||
input wire touch_event,
|
||||
|
||||
input wire app_gpio1,
|
||||
input wire app_gpio2,
|
||||
output wire app_gpio3,
|
||||
output wire app_gpio4,
|
||||
|
||||
output wire led_r,
|
||||
output wire led_g,
|
||||
output wire led_b
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Local parameters
|
||||
//----------------------------------------------------------------
|
||||
// Top level mem area prefixes.
|
||||
localparam ROM_PREFIX = 2'h0;
|
||||
localparam RAM_PREFIX = 2'h1;
|
||||
localparam RESERVED_PREFIX = 2'h2;
|
||||
localparam MMIO_PREFIX = 2'h3;
|
||||
|
||||
// MMIO core mem sub-prefixes.
|
||||
localparam TRNG_PREFIX = 6'h00;
|
||||
localparam TIMER_PREFIX = 6'h01;
|
||||
localparam UDS_PREFIX = 6'h02;
|
||||
localparam UART_PREFIX = 6'h03;
|
||||
localparam TOUCH_SENSE_PREFIX = 6'h04;
|
||||
localparam MTA1_PREFIX = 6'h3f;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Registers, memories with associated wires.
|
||||
//----------------------------------------------------------------
|
||||
reg [31 : 0] muxed_rdata_reg;
|
||||
reg [31 : 0] muxed_rdata_new;
|
||||
|
||||
reg muxed_ready_reg;
|
||||
reg muxed_ready_new;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Wires.
|
||||
//----------------------------------------------------------------
|
||||
wire reset_n;
|
||||
|
||||
wire cpu_valid;
|
||||
wire [03 : 0] cpu_wstrb;
|
||||
wire [31 : 0] cpu_addr;
|
||||
wire [31 : 0] cpu_wdata;
|
||||
|
||||
/* verilator lint_off UNOPTFLAT */
|
||||
reg rom_cs;
|
||||
/* verilator lint_on UNOPTFLAT */
|
||||
reg [11 : 0] rom_address;
|
||||
wire [31 : 0] rom_read_data;
|
||||
wire rom_ready;
|
||||
|
||||
reg ram_cs;
|
||||
reg [3 : 0] ram_we;
|
||||
reg [14 : 0] ram_address;
|
||||
reg [31 : 0] ram_write_data;
|
||||
wire [31 : 0] ram_read_data;
|
||||
wire ram_ready;
|
||||
|
||||
/* verilator lint_off UNOPTFLAT */
|
||||
reg trng_cs;
|
||||
/* verilator lint_on UNOPTFLAT */
|
||||
reg trng_we;
|
||||
reg [7 : 0] trng_address;
|
||||
reg [31 : 0] trng_write_data;
|
||||
wire [31 : 0] trng_read_data;
|
||||
wire trng_ready;
|
||||
|
||||
/* verilator lint_off UNOPTFLAT */
|
||||
reg timer_cs;
|
||||
/* verilator lint_on UNOPTFLAT */
|
||||
reg timer_we;
|
||||
reg [7 : 0] timer_address;
|
||||
reg [31 : 0] timer_write_data;
|
||||
wire [31 : 0] timer_read_data;
|
||||
wire timer_ready;
|
||||
|
||||
/* verilator lint_off UNOPTFLAT */
|
||||
reg uds_cs;
|
||||
/* verilator lint_on UNOPTFLAT */
|
||||
reg [7 : 0] uds_address;
|
||||
wire [31 : 0] uds_read_data;
|
||||
wire uds_ready;
|
||||
|
||||
/* verilator lint_off UNOPTFLAT */
|
||||
reg uart_cs;
|
||||
/* verilator lint_on UNOPTFLAT */
|
||||
reg uart_we;
|
||||
reg [7 : 0] uart_address;
|
||||
reg [31 : 0] uart_write_data;
|
||||
wire [31 : 0] uart_read_data;
|
||||
wire uart_ready;
|
||||
|
||||
/* verilator lint_off UNOPTFLAT */
|
||||
reg touch_sense_cs;
|
||||
/* verilator lint_on UNOPTFLAT */
|
||||
reg touch_sense_we;
|
||||
reg [7 : 0] touch_sense_address;
|
||||
wire [31 : 0] touch_sense_read_data;
|
||||
wire touch_sense_ready;
|
||||
|
||||
/* verilator lint_off UNOPTFLAT */
|
||||
reg mta1_cs;
|
||||
/* verilator lint_on UNOPTFLAT */
|
||||
reg mta1_we;
|
||||
reg [7 : 0] mta1_address;
|
||||
reg [31 : 0] mta1_write_data;
|
||||
wire [31 : 0] mta1_read_data;
|
||||
wire mta1_ready;
|
||||
wire fw_app_mode;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Concurrent assignments.
|
||||
//----------------------------------------------------------------
|
||||
assign valid = cpu_valid;
|
||||
assign wstrb = cpu_wstrb;
|
||||
assign addr = cpu_addr;
|
||||
assign wdata = cpu_wdata;
|
||||
assign rdata = muxed_rdata_reg;
|
||||
assign ready = muxed_ready_reg;
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Module instantiations.
|
||||
//----------------------------------------------------------------
|
||||
reset_gen #(.RESET_CYCLES(200))
|
||||
reset_gen_inst(.clk(clk), .rst_n(reset_n));
|
||||
|
||||
|
||||
picorv32 #(
|
||||
.ENABLE_COUNTERS(0),
|
||||
.LATCHED_MEM_RDATA(0),
|
||||
.TWO_STAGE_SHIFT(0),
|
||||
.TWO_CYCLE_ALU(0),
|
||||
.CATCH_MISALIGN(0),
|
||||
.CATCH_ILLINSN(0),
|
||||
.COMPRESSED_ISA(1),
|
||||
.ENABLE_MUL(1),
|
||||
.ENABLE_DIV(0),
|
||||
.BARREL_SHIFTER(0)
|
||||
) cpu(
|
||||
.clk(clk),
|
||||
.resetn(reset_n),
|
||||
|
||||
.mem_valid(cpu_valid),
|
||||
.mem_addr (cpu_addr),
|
||||
.mem_wdata(cpu_wdata),
|
||||
.mem_wstrb(cpu_wstrb),
|
||||
.mem_rdata(muxed_rdata_reg),
|
||||
.mem_ready(muxed_ready_reg),
|
||||
|
||||
// Defined unsed ports. Makes lint happy,
|
||||
// but still needs to help lint with empty ports.
|
||||
/* verilator lint_off PINCONNECTEMPTY */
|
||||
.irq(32'h0),
|
||||
.eoi(),
|
||||
.trap(),
|
||||
.trace_valid(),
|
||||
.trace_data(),
|
||||
.mem_instr(),
|
||||
.mem_la_read(),
|
||||
.mem_la_write(),
|
||||
.mem_la_addr(),
|
||||
.mem_la_wdata(),
|
||||
.mem_la_wstrb(),
|
||||
.pcpi_valid(),
|
||||
.pcpi_insn(),
|
||||
.pcpi_rs1(),
|
||||
.pcpi_rs2(),
|
||||
.pcpi_wr(1'h0),
|
||||
.pcpi_rd(32'h0),
|
||||
.pcpi_wait(1'h0),
|
||||
.pcpi_ready(1'h0)
|
||||
/* verilator lint_on PINCONNECTEMPTY */
|
||||
);
|
||||
|
||||
|
||||
rom rom_inst(
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.cs(rom_cs),
|
||||
.address(rom_address),
|
||||
.read_data(rom_read_data),
|
||||
.ready(rom_ready)
|
||||
);
|
||||
|
||||
|
||||
ram ram_inst(
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.cs(ram_cs),
|
||||
.we(ram_we),
|
||||
.address(ram_address),
|
||||
.write_data(ram_write_data),
|
||||
.read_data(ram_read_data),
|
||||
.ready(ram_ready)
|
||||
);
|
||||
|
||||
|
||||
timer timer_inst(
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.cs(timer_cs),
|
||||
.we(timer_we),
|
||||
.address(timer_address),
|
||||
.write_data(timer_write_data),
|
||||
.read_data(timer_read_data),
|
||||
.ready(timer_ready)
|
||||
);
|
||||
|
||||
|
||||
uds uds_inst(
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.cs(uds_cs),
|
||||
.address(uds_address),
|
||||
.read_data(uds_read_data),
|
||||
.ready(uds_ready)
|
||||
);
|
||||
|
||||
|
||||
uart uart_inst(
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.rxd(interface_tx),
|
||||
.txd(interface_rx),
|
||||
|
||||
.cs(uart_cs),
|
||||
.we(uart_we),
|
||||
.address(uart_address),
|
||||
.write_data(uart_write_data),
|
||||
.read_data(uart_read_data),
|
||||
.ready(uart_ready)
|
||||
);
|
||||
|
||||
|
||||
touch_sense touch_sense_inst(
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.touch_event(touch_event),
|
||||
|
||||
.cs(touch_sense_cs),
|
||||
.we(touch_sense_we),
|
||||
.address(touch_sense_address),
|
||||
.read_data(touch_sense_read_data),
|
||||
.ready(touch_sense_ready)
|
||||
);
|
||||
|
||||
|
||||
mta1 mta1_inst(
|
||||
.clk(clk),
|
||||
.reset_n(reset_n),
|
||||
|
||||
.fw_app_mode(fw_app_mode),
|
||||
|
||||
.led_r(led_r),
|
||||
.led_g(led_g),
|
||||
.led_b(led_b),
|
||||
|
||||
.gpio1(app_gpio1),
|
||||
.gpio2(app_gpio2),
|
||||
.gpio3(app_gpio3),
|
||||
.gpio4(app_gpio4),
|
||||
|
||||
.cs(mta1_cs),
|
||||
.we(mta1_we),
|
||||
.address(mta1_address),
|
||||
.write_data(mta1_write_data),
|
||||
.read_data(mta1_read_data),
|
||||
.ready(mta1_ready)
|
||||
);
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// Reg_update.
|
||||
// Posedge triggered with synchronous, active low reset.
|
||||
//----------------------------------------------------------------
|
||||
always @(posedge clk)
|
||||
begin : reg_update
|
||||
if (!reset_n) begin
|
||||
muxed_ready_reg <= 1'h0;
|
||||
muxed_rdata_reg <= 32'h0;
|
||||
end
|
||||
|
||||
else begin
|
||||
muxed_ready_reg <= muxed_ready_new;
|
||||
muxed_rdata_reg <= muxed_rdata_new;
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
//----------------------------------------------------------------
|
||||
// cpu_mem_ctrl
|
||||
// CPU memory decode and control logic.
|
||||
//----------------------------------------------------------------
|
||||
always @*
|
||||
begin : cpu_mem_ctrl
|
||||
reg [1 : 0] area_prefix;
|
||||
reg [5 : 0] core_prefix;
|
||||
|
||||
area_prefix = cpu_addr[31 : 30];
|
||||
core_prefix = cpu_addr[29 : 24];
|
||||
|
||||
muxed_ready_new = 1'h0;
|
||||
muxed_rdata_new = 32'h0;
|
||||
|
||||
rom_cs = 1'h0;
|
||||
rom_address = cpu_addr[13 : 2];
|
||||
|
||||
ram_cs = 1'h0;
|
||||
ram_we = cpu_wstrb;
|
||||
ram_address = cpu_addr[16 : 2];
|
||||
ram_write_data = cpu_wdata;
|
||||
|
||||
trng_cs = 1'h0;
|
||||
trng_we = |cpu_wstrb;
|
||||
trng_address = cpu_addr[10 : 2];
|
||||
trng_write_data = cpu_wdata;
|
||||
|
||||
timer_cs = 1'h0;
|
||||
timer_we = |cpu_wstrb;
|
||||
timer_address = cpu_addr[10 : 2];
|
||||
timer_write_data = cpu_wdata;
|
||||
|
||||
uds_cs = 1'h0;
|
||||
uds_address = cpu_addr[10 : 2];
|
||||
|
||||
uart_cs = 1'h0;
|
||||
uart_we = |cpu_wstrb;
|
||||
uart_address = cpu_addr[10 : 2];
|
||||
uart_write_data = cpu_wdata;
|
||||
|
||||
touch_sense_cs = 1'h0;
|
||||
touch_sense_we = |cpu_wstrb;
|
||||
touch_sense_address = cpu_addr[10 : 2];
|
||||
|
||||
mta1_cs = 1'h0;
|
||||
mta1_we = |cpu_wstrb;
|
||||
mta1_address = cpu_addr[10 : 2];
|
||||
mta1_write_data = cpu_wdata;
|
||||
|
||||
if (cpu_valid && !muxed_ready_reg) begin
|
||||
case (area_prefix)
|
||||
ROM_PREFIX: begin
|
||||
`verbose($display("Access to ROM area");)
|
||||
rom_cs = 1'h1;
|
||||
muxed_rdata_new = rom_read_data;
|
||||
muxed_ready_new = rom_ready;
|
||||
end
|
||||
|
||||
RAM_PREFIX: begin
|
||||
`verbose($display("Access to RAM area");)
|
||||
ram_cs = 1'h1;
|
||||
muxed_rdata_new = ram_read_data;
|
||||
muxed_ready_new = ram_ready;
|
||||
end
|
||||
|
||||
RESERVED_PREFIX: begin
|
||||
`verbose($display("Access to RESERVED area");)
|
||||
muxed_rdata_new = 32'h00000000;
|
||||
muxed_ready_new = 1'h1;
|
||||
end
|
||||
|
||||
MMIO_PREFIX: begin
|
||||
`verbose($display("Access to MMIO area");)
|
||||
case (core_prefix)
|
||||
TRNG_PREFIX: begin
|
||||
`verbose($display("Access to TRNG core");)
|
||||
trng_cs = 1'h1;
|
||||
muxed_rdata_new = trng_read_data;
|
||||
muxed_ready_new = trng_ready;
|
||||
end
|
||||
|
||||
TIMER_PREFIX: begin
|
||||
`verbose($display("Access to TIMER core");)
|
||||
timer_cs = 1'h1;
|
||||
muxed_rdata_new = timer_read_data;
|
||||
muxed_ready_new = timer_ready;
|
||||
end
|
||||
|
||||
UDS_PREFIX: begin
|
||||
`verbose($display("Access to UDS core");)
|
||||
uds_cs = 1'h1;
|
||||
muxed_rdata_new = uds_read_data;
|
||||
muxed_ready_new = uds_ready;
|
||||
end
|
||||
|
||||
UART_PREFIX: begin
|
||||
`verbose($display("Access to UART core");)
|
||||
uart_cs = 1'h1;
|
||||
muxed_rdata_new = uart_read_data;
|
||||
muxed_ready_new = uart_ready;
|
||||
end
|
||||
|
||||
TOUCH_SENSE_PREFIX: begin
|
||||
`verbose($display("Access to TOUCH_SENSE core");)
|
||||
touch_sense_cs = 1'h1;
|
||||
muxed_rdata_new = touch_sense_read_data;
|
||||
muxed_ready_new = touch_sense_ready;
|
||||
end
|
||||
|
||||
MTA1_PREFIX: begin
|
||||
`verbose($display("Access to MTA1 core");)
|
||||
mta1_cs = 1'h1;
|
||||
muxed_rdata_new = mta1_read_data;
|
||||
muxed_ready_new = mta1_ready;
|
||||
end
|
||||
|
||||
default: begin
|
||||
`verbose($display("UNDEFINED MMIO");)
|
||||
muxed_rdata_new = 32'h00000000;
|
||||
muxed_ready_new = 1'h1;
|
||||
end
|
||||
endcase // case (core_prefix)
|
||||
end // case: MMIO_PREFIX
|
||||
|
||||
default: begin
|
||||
`verbose($display("UNDEFINED AREA");)
|
||||
muxed_rdata_new = 32'h0;
|
||||
muxed_ready_new = 1'h1;
|
||||
end
|
||||
endcase // case (area_prefix)
|
||||
end
|
||||
end
|
||||
|
||||
endmodule // application_fpga
|
||||
|
||||
//======================================================================
|
||||
// EOF application_fpga.v
|
||||
//======================================================================
|
26
hw/application_fpga/tools/makehex/makehex.py
Normal file
26
hw/application_fpga/tools/makehex/makehex.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# This is free and unencumbered software released into the public domain.
|
||||
#
|
||||
# Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||
# distribute this software, either in source code form or as a compiled
|
||||
# binary, for any purpose, commercial or non-commercial, and by any
|
||||
# means.
|
||||
|
||||
from sys import argv
|
||||
|
||||
binfile = argv[1]
|
||||
nwords = int(argv[2])
|
||||
|
||||
with open(binfile, "rb") as f:
|
||||
bindata = f.read()
|
||||
|
||||
assert len(bindata) < 4*nwords
|
||||
assert len(bindata) % 4 == 0
|
||||
|
||||
for i in range(nwords):
|
||||
if i < len(bindata) // 4:
|
||||
w = bindata[4*i : 4*i+4]
|
||||
print("%02x%02x%02x%02x" % (w[3], w[2], w[1], w[0]))
|
||||
else:
|
||||
print("0")
|
8
hw/application_fpga/tools/tpt/README.md
Normal file
8
hw/application_fpga/tools/tpt/README.md
Normal file
|
@ -0,0 +1,8 @@
|
|||
# Tillitis Key Provisioning Tool
|
||||
|
||||
## Introduction
|
||||
Tillis Key Provisioning Tool (tpt) is a program for generating the 32 byte Unique Device Secret (UDS). The tool will also generate the 8 byte Unique Device Identity. Both the UDS and the UDI are injected into the FPGA bitstream file during build.
|
||||
|
||||
The UDS is generated using HKDF (RFC 5869), and the user is expected to supply a secret as part of the input to the HKDF Extract operation. The Input Keying Material is generated by extracting 256 bytes using the Python secrets module.
|
||||
|
||||
The tool uses [python-hkdf](https://github.com/casebeer/python-hkdf).
|
0
hw/application_fpga/tools/tpt/__init__.py
Normal file
0
hw/application_fpga/tools/tpt/__init__.py
Normal file
95
hw/application_fpga/tools/tpt/hkdf.py
Normal file
95
hw/application_fpga/tools/tpt/hkdf.py
Normal file
|
@ -0,0 +1,95 @@
|
|||
# Copyright (c) 2012 Christopher H. Casebeer. All rights reserved.
|
||||
# SPDX-License-Identifier: BSD-2
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
from __future__ import division
|
||||
|
||||
import hmac
|
||||
import hashlib
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] == 3:
|
||||
buffer = lambda x: x
|
||||
|
||||
def hkdf_extract(salt, input_key_material, hash=hashlib.sha512):
|
||||
'''
|
||||
Extract a pseudorandom key suitable for use with hkdf_expand
|
||||
from the input_key_material and a salt using HMAC with the
|
||||
provided hash (default SHA-512).
|
||||
|
||||
salt should be a random, application-specific byte string. If
|
||||
salt is None or the empty string, an all-zeros string of the same
|
||||
length as the hash's block size will be used instead per the RFC.
|
||||
|
||||
See the HKDF draft RFC and paper for usage notes.
|
||||
'''
|
||||
hash_len = hash().digest_size
|
||||
if salt == None or len(salt) == 0:
|
||||
salt = bytearray((0,) * hash_len)
|
||||
return hmac.new(bytes(salt), buffer(input_key_material), hash).digest()
|
||||
|
||||
def hkdf_expand(pseudo_random_key, info=b"", length=32, hash=hashlib.sha512):
|
||||
'''
|
||||
Expand `pseudo_random_key` and `info` into a key of length `bytes` using
|
||||
HKDF's expand function based on HMAC with the provided hash (default
|
||||
SHA-512). See the HKDF draft RFC and paper for usage notes.
|
||||
'''
|
||||
hash_len = hash().digest_size
|
||||
length = int(length)
|
||||
if length > 255 * hash_len:
|
||||
raise Exception("Cannot expand to more than 255 * %d = %d bytes using the specified hash function" %\
|
||||
(hash_len, 255 * hash_len))
|
||||
blocks_needed = length // hash_len + (0 if length % hash_len == 0 else 1) # ceil
|
||||
okm = b""
|
||||
output_block = b""
|
||||
for counter in range(blocks_needed):
|
||||
output_block = hmac.new(pseudo_random_key, buffer(output_block + info + bytearray((counter + 1,))),\
|
||||
hash).digest()
|
||||
okm += output_block
|
||||
return okm[:length]
|
||||
|
||||
class Hkdf(object):
|
||||
'''
|
||||
Wrapper class for HKDF extract and expand functions
|
||||
'''
|
||||
def __init__(self, salt, input_key_material, hash=hashlib.sha256):
|
||||
'''
|
||||
Extract a pseudorandom key from `salt` and `input_key_material` arguments.
|
||||
|
||||
See the HKDF draft RFC for guidance on setting these values. The constructor
|
||||
optionally takes a `hash` arugment defining the hash function use,
|
||||
defaulting to hashlib.sha256.
|
||||
'''
|
||||
self._hash = hash
|
||||
self._prk = hkdf_extract(salt, input_key_material, self._hash)
|
||||
def expand(self, info=b"", length=32):
|
||||
'''
|
||||
Generate output key material based on an `info` value
|
||||
|
||||
Arguments:
|
||||
- info - context to generate the OKM
|
||||
- length - length in bytes of the key to generate
|
||||
|
||||
See the HKDF draft RFC for guidance.
|
||||
'''
|
||||
return hkdf_expand(self._prk, info, length, self._hash)
|
129
hw/application_fpga/tools/tpt/tpt.py
Executable file
129
hw/application_fpga/tools/tpt/tpt.py
Executable file
|
@ -0,0 +1,129 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
#=======================================================================
|
||||
#
|
||||
# tpt.py
|
||||
# ------
|
||||
# TillitisKey Provisioning Tool.
|
||||
#
|
||||
# The tool use HKDF (RFC5869) to generate the UDS.
|
||||
#
|
||||
# Copyright (C) 2022 - Tillitis AB
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
#=======================================================================
|
||||
|
||||
import sys
|
||||
import argparse
|
||||
from secrets import token_bytes
|
||||
from binascii import unhexlify
|
||||
from hkdf import Hkdf
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
#-------------------------------------------------------------------
|
||||
def gen_uds(verbose, arg_uss):
|
||||
if verbose:
|
||||
print("\nGenerating UDS")
|
||||
|
||||
if arg_uss == None:
|
||||
uss = str.encode(input("Enter user supplied secret: "))
|
||||
else:
|
||||
uss = str.encode(arg_uss)
|
||||
|
||||
ikm = token_bytes(256)
|
||||
my_hkdf = Hkdf(uss, ikm)
|
||||
uds = my_hkdf.expand(b"TillitisKey UDS", 32)
|
||||
uds_hex = uds.hex()
|
||||
|
||||
if verbose:
|
||||
print("\nGenerated UDS:")
|
||||
print(uds_hex, "\n")
|
||||
|
||||
return uds_hex
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
#-------------------------------------------------------------------
|
||||
def save_uds(verbose, uds):
|
||||
if verbose:
|
||||
print("Writing uds.hex")
|
||||
|
||||
with open ("uds.hex", 'w', encoding = 'utf-8') as uds_file:
|
||||
for i in range(8):
|
||||
uds_file.write(uds[(i*8) : (i*8 + 8)]+"\n")
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
#-------------------------------------------------------------------
|
||||
def gen_udi(verbose, pid, vid, rev, serial):
|
||||
if verbose:
|
||||
print("Generating UDI")
|
||||
|
||||
if vid == None:
|
||||
vid = int(input("Enter Vendor ID (0 - 65535): "))
|
||||
|
||||
if pid == None:
|
||||
pid = int(input("Enter Product ID (0 - 255): "))
|
||||
|
||||
if rev == None:
|
||||
rev = int(input("Enter revision (0 - 15): "))
|
||||
|
||||
if serial == None:
|
||||
serial = int(input("Enter serial number (0 - (2**32 -1)): "))
|
||||
|
||||
assert vid < 65536
|
||||
assert pid < 256
|
||||
assert rev < 16
|
||||
assert serial < 2**32
|
||||
|
||||
udi_hex = ("0%04x%02x%1x%08x" % (vid, pid, rev, serial))
|
||||
|
||||
if verbose:
|
||||
print("\nGenerated UDI:")
|
||||
print(udi_hex, "\n")
|
||||
|
||||
return udi_hex
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
#-------------------------------------------------------------------
|
||||
def save_udi(verbose, udi):
|
||||
if verbose:
|
||||
print("Writing udi.hex")
|
||||
|
||||
with open ("udi.hex", 'w', encoding = 'utf-8') as udi_file:
|
||||
udi_file.write(udi[0 : 8] + "\n")
|
||||
udi_file.write(udi[8 : 16] + "\n")
|
||||
|
||||
def enc_str(b):
|
||||
return bytestring.decode(sys.getfilesystemencoding())
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
#-------------------------------------------------------------------
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("-v", "--verbose", help="Verbose operation", action="store_true")
|
||||
parser.add_argument("--uss", help="User supplied secret", type=str)
|
||||
parser.add_argument("--vid", help="Vendor id (0 - 65535)", type=int)
|
||||
parser.add_argument("--pid", help="Product id (0 - 2555", type=int)
|
||||
parser.add_argument("--rev", help="Revision number (0 - 15)", type=int)
|
||||
parser.add_argument("--serial", help="Serial number (0 - (2**31 - 1))", type=int)
|
||||
args = parser.parse_args()
|
||||
|
||||
print(args)
|
||||
|
||||
if args.verbose:
|
||||
print("TillitisKey Provisining Tool (TPT)")
|
||||
|
||||
uds = gen_uds(args.verbose, args.uss)
|
||||
save_uds(args.verbose, uds)
|
||||
|
||||
udi = gen_udi(args.verbose, args.pid, args.vid, args.rev, args.serial)
|
||||
save_udi(args.verbose, udi)
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
#-------------------------------------------------------------------
|
||||
if __name__=="__main__":
|
||||
sys.exit(main())
|
Loading…
Add table
Add a link
Reference in a new issue