Make initial public release

This commit is contained in:
Joachim Strömbergson 2022-09-19 08:51:11 +02:00 committed by Daniel Lublin
commit 715de60f4a
251 changed files with 881225 additions and 0 deletions

View file

@ -0,0 +1,4 @@
# mta1
## Introduction
Top level core that provides chip info, debug support and chip level control.

View 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
//======================================================================

View 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.

View 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.

File diff suppressed because it is too large Load diff

View 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.

View 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
//======================================================================

View 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
//======================================================================

View 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
//======================================================================

View 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
//======================================================================

View 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
#===================================================================

View 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.

View 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
//======================================================================

View 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
//======================================================================

View 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
#===================================================================

View 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)

View 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
//======================================================================

View 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
//======================================================================

View 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
//======================================================================

View 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
//======================================================================

View 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.

View 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.

View 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
//======================================================================

View 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
//======================================================================

View 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
//======================================================================

View 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
//======================================================================

View 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.

View 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
//======================================================================

View 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
//======================================================================

View 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
#===================================================================