diff --git a/dev/hdl/main.sv b/dev/hdl/main.sv index 603acbb..d2230f1 100644 --- a/dev/hdl/main.sv +++ b/dev/hdl/main.sv @@ -7,9 +7,13 @@ module main#( // ROM image file. - parameter string rom_file = "", + parameter string rom_file = "", // UART buffer size. - parameter integer uart_buf = 16 + parameter integer uart_buf = 16, + // Default UART clock divider. + parameter integer uart_div = 1250, + // Whether we're running in the simulator. + parameter bit is_simulator = 0 )( // CPU clock. input logic clk, @@ -18,8 +22,6 @@ module main#( // Synchronous reset. input logic rst, - // UART clock. - input logic uart_clk, // UART send data. output logic txd, // UART receive data. @@ -32,6 +34,9 @@ module main#( // GPIO inputs. input logic[31:0] gpio_in, + // A 32-bit quantity of randomness. + input logic[31:0] randomness, + // Power management unit interface. pmu_bus.CPU pmb ); @@ -42,7 +47,7 @@ module main#( boa_mem_bus dbus(); boa_mem_bus mux_a_bus[2](); boa_mem_bus mux_b_bus[3](); - boa_mem_bus#(.alen(12)) peri_bus[3](); + boa_mem_bus#(.alen(12)) peri_bus[13](); // Program ROM. dp_block_ram#(10, rom_file, 1) rom(clk, mux_a_bus[0], mux_b_bus[0]); @@ -51,18 +56,34 @@ module main#( // UART. logic rx_full, tx_empty; - boa_peri_uart#(.addr('h000), .tx_depth(uart_buf), .rx_depth(uart_buf)) uart( - clk, rst, peri_bus[0], uart_clk, txd, rxd, tx_empty, rx_full + boa_peri_uart#(.addr('h000), .tx_depth(uart_buf), .rx_depth(uart_buf), .init_div(uart_div)) uart( + clk, rst, peri_bus[0], txd, rxd, tx_empty, rx_full ); // PMU interface. boa_peri_pmu #(.addr('h100)) pmu (clk, rst, peri_bus[1], pmb); // GPIO. - boa_peri_gpio#(.addr('h200)) gpio(clk, rst, peri_bus[2], clk, 1, gpio_out, gpio_oe, gpio_in); + logic[7:0] gpio_ext_sig; + logic[7:0] gpio_ext_oe; + boa_peri_gpio#(.addr('h200), .num_ext(8)) gpio(clk, rst, peri_bus[2], gpio_ext_sig, gpio_ext_oe, gpio_out, gpio_oe, gpio_in); + // Hardware RNG. + boa_peri_readable#(.addr('h300)) rng(clk, rst, peri_bus[3], randomness); + // PWM generators. + assign gpio_ext_oe[7:0] = 8'hff; + boa_peri_pwm#(.addr('h480)) pwm0gen(clk, clk, rst, peri_bus[4+0], gpio_ext_sig[0]); + boa_peri_pwm#(.addr('h490)) pwm1gen(clk, clk, rst, peri_bus[4+1], gpio_ext_sig[1]); + boa_peri_pwm#(.addr('h4a0)) pwm2gen(clk, clk, rst, peri_bus[4+2], gpio_ext_sig[2]); + boa_peri_pwm#(.addr('h4b0)) pwm3gen(clk, clk, rst, peri_bus[4+3], gpio_ext_sig[3]); + boa_peri_pwm#(.addr('h4c0)) pwm4gen(clk, clk, rst, peri_bus[4+4], gpio_ext_sig[4]); + boa_peri_pwm#(.addr('h4d0)) pwm5gen(clk, clk, rst, peri_bus[4+5], gpio_ext_sig[5]); + boa_peri_pwm#(.addr('h4e0)) pwm6gen(clk, clk, rst, peri_bus[4+6], gpio_ext_sig[6]); + boa_peri_pwm#(.addr('h4f0)) pwm7gen(clk, clk, rst, peri_bus[4+7], gpio_ext_sig[7]); + // Is simulator? + boa_peri_readable#(.addr('hffc)) is_sim(clk, rst, peri_bus[12], is_simulator); // Memory interconnects. boa_mem_mux#(.mems(2)) mux_a(clk, rst, pbus, mux_a_bus, {32'h40001000, 32'h40010000}, {12, 16}); boa_mem_mux#(.mems(3)) mux_b(clk, rst, dbus, mux_b_bus, {32'h40001000, 32'h40010000, 32'h80000000}, {12, 16, 12}); - boa_mem_overlay#(.mems(3)) ovl(mux_b_bus[2], peri_bus); + boa_mem_overlay#(.mems(13)) ovl(mux_b_bus[2], peri_bus); // CPU. logic[31:16] irq; diff --git a/dev/hdl/gpio.sv b/dev/hdl/mmio/gpio.sv similarity index 89% rename from dev/hdl/gpio.sv rename to dev/hdl/mmio/gpio.sv index 4295822..e141e76 100644 --- a/dev/hdl/gpio.sv +++ b/dev/hdl/mmio/gpio.sv @@ -57,7 +57,16 @@ module boa_peri_gpio#( logic[pins-1:0] pin_in_reg; always @(posedge clk) begin pin_in_reg <= pin_in; - if (bus.addr<<2 == addr) begin + if (rst) begin + integer i; + out_reg <= 0; + oe_reg <= 0; + for (i = 0; i < pins; i = i + 1) begin + sel_reg[i] <= 0; + ext_reg[i] <= 0; + end + bus.rdata <= 0; + end else if (bus.addr<<2 == addr) begin // Parallel output. bus.rdata <= pin_in_reg; if (bus.we == 15) begin diff --git a/dev/hdl/i2c.sv b/dev/hdl/mmio/i2c.sv similarity index 100% rename from dev/hdl/i2c.sv rename to dev/hdl/mmio/i2c.sv diff --git a/dev/hdl/pmu.sv b/dev/hdl/mmio/pmu.sv similarity index 100% rename from dev/hdl/pmu.sv rename to dev/hdl/mmio/pmu.sv diff --git a/dev/hdl/mmio/pwm.sv b/dev/hdl/mmio/pwm.sv new file mode 100644 index 0000000..3e520e1 --- /dev/null +++ b/dev/hdl/mmio/pwm.sv @@ -0,0 +1,48 @@ + +// Copyright © 2023, Julian Scheffers, see LICENSE for more information + +`timescale 1ns/1ps + + + +// A PWM generator intended for use with GPIO signals. +module boa_peri_pwm#( + // Base address to respond to. + parameter addr = 32'h8000_0000 +)( + // CPU clock. + input logic clk, + // PWM clock. + input logic pwm_clk, + // Synchronous reset. + input logic rst, + + // Peripheral bus. + boa_mem_bus.MEM bus, + + // PWM value. + output logic pwm +); + // Configuration register. + logic[31:0] cfg; + boa_peri_writeable#(addr) cfg_reg(clk, rst, bus, cfg); + wire[7:0] pwm_val = cfg[7:0]; + wire[15:0] pwm_div = cfg[31:15]; + + // PWM generator. + logic[15:0] div = 1; + logic[7:0] val; + always @(posedge pwm_clk) begin + if (div == 1) begin + div <= pwm_div ? pwm_div : 1; + val <= val + 1; + if (val == 0) begin + pwm <= pwm_val != 0; + end else if (val == pwm_val) begin + pwm <= 0; + end + end else begin + div <= div - 1; + end + end +endmodule diff --git a/dev/hdl/mmio/readable.sv b/dev/hdl/mmio/readable.sv new file mode 100644 index 0000000..fa2b4d4 --- /dev/null +++ b/dev/hdl/mmio/readable.sv @@ -0,0 +1,32 @@ + +// Copyright © 2023, Julian Scheffers, see LICENSE for more information + +`timescale 1ns/1ps + + + +// A single word of read-only MMIO. +module boa_peri_readable#( + // Base address to respond to. + parameter addr = 32'h8000_0000 +)( + // CPU clock. + input logic clk, + // Synchronous reset. + input logic rst, + + // Peripheral bus. + boa_mem_bus.MEM bus, + + // Value to present to the bus. + input logic[31:0] value +); + assign bus.ready = 1; + always @(posedge clk) begin + if (bus.addr == addr[bus.alen-1:2]) begin + bus.rdata <= value; + end else begin + bus.rdata <= 0; + end + end +endmodule diff --git a/dev/hdl/mmio/uart.sv b/dev/hdl/mmio/uart.sv new file mode 100644 index 0000000..76a685e --- /dev/null +++ b/dev/hdl/mmio/uart.sv @@ -0,0 +1,257 @@ + +// Copyright © 2023, Julian Scheffers, see LICENSE for more information + +`timescale 1ns/1ps + + + +// Unbuffered supersampling UART transmitter. +// Divides the clock by clk_div for transmitting. +module unbuffered_uart_tx#( + // Number of bits for the clock divider input. + parameter dlen = 8 +)( + // Undivided clock. + input logic clk, + // Synchronous reset. + input logic rst, + // Clock divider setting. + input logic[dlen-1:0] clk_div, + + // Byte to transmit. + input logic[7:0] tx_byte, + // Send trigger. + input logic tx_trig, + // Send acknowledgement. + output logic tx_ack, + // UART transmit wire. + output logic txd, + // UART is currently transmitting. + output logic tx_busy +); + // Clock division counter. + logic[dlen-1:0] timer = 1; + // Counts number of sent bits. + logic[3:0] state; + // Buffer for sending data. + logic[8:0] tx_buf = 9'h1ff; + // TX data is LSB of TX buffer. + assign txd = tx_buf[0]; + // Transmitter is busy if state is nonzero. + assign tx_busy = state != 0; + + always @(posedge clk) begin + if (rst) begin + // Reset. + timer <= 1; + state <= 0; + tx_buf <= 9'h1ff; + tx_ack <= 0; + end else if (timer == 1) begin + timer <= clk_div ? clk_div : 1; + if (state == 0 && tx_trig) begin + // Initialise transmitter state. + tx_buf[0] <= 0; + tx_buf[8:1] <= tx_byte; + state <= 1; + tx_ack <= 1; + end else if (state == 9) begin + // Finalise transmitter state. + state <= 0; + tx_buf <= (tx_buf >> 1) | 9'h100; + tx_ack <= tx_ack && tx_trig; + end else if (state != 0) begin + // Shift send buffer. + state <= state + 1; + tx_buf <= (tx_buf >> 1) | 9'h100; + tx_ack <= tx_ack && tx_trig; + end + end else begin + timer <= timer - 1; + tx_ack <= tx_ack && tx_trig; + end + end +endmodule + +// Unbuffered supersampling UART receiver. +// Divides the clock by clk_div for receiving. +module unbuffered_uart_rx#( + // Number of bits for the clock divider input. + parameter dlen = 8 +)( + // Undivided clock. + input logic clk, + // Synchronous reset. + input logic rst, + // Clock divider setting. + input logic[dlen-1:0] clk_div, + + // Byte just received. + output logic[7:0] rx_byte, + // A byte has been fully received. + output logic rx_trig, + // Acknowledge and clear rx_trig. + input logic rx_ack, + // UART receive wire. + input logic rxd, + // UART is currently receiving. + output logic rx_busy +); + // Clock division counter. + logic[dlen-1:0] timer = 1; + // Counts number of received bits. + logic[3:0] state; + // Buffer for receiving data. + logic[7:0] rx_buf; + // RX is busy if state is nonzero. + assign rx_busy = state != 0; + + always @(posedge clk) begin + if (rst) begin + // Reset. + timer <= 1; + state <= 0; + rx_buf <= 0; + rx_trig <= 0; + end else if (state == 0 && !rxd) begin + // UART start bit. + state <= 1; + timer <= (clk_div >> 1) ? (clk_div >> 1) : 1; + rx_trig <= rx_trig && !rx_ack; + end else if (state != 0 && timer == 1) begin + timer <= clk_div ? clk_div : 1; + if (state == 10) begin + // UART stop bit. + if (rxd) begin + rx_trig <= rxd; + rx_byte <= rx_buf; + end else begin + rx_trig <= rx_trig && !rx_ack; + end + state <= 0; + end else begin + // Shift receive buffer. + rx_buf <= {rxd, rx_buf[7:1]}; + state <= state + 1; + rx_trig <= rx_trig && !rx_ack; + end + end else if (state != 0) begin + timer <= timer - 1; + rx_trig <= rx_trig && !rx_ack; + end else begin + rx_trig <= rx_trig && !rx_ack; + end + end +endmodule + +// Basic UART with FIFO peripheral. +// Configurable clock divider. +module boa_peri_uart#( + // Base address to respond to. + parameter addr = 32'h8000_0000, + // TX buffer depth, must be a power of 2 >= 4. + parameter tx_depth = 4, + // RX buffer depth, must be a power of 2 >= 4. + parameter rx_depth = 4, + // Stall writes on TX buffer full. + parameter tx_full_stall = 1, + // Number of bits for the clock divider input. + parameter dlen = 16, + // Default clock divider value. + parameter init_div = 1250 +)( + // Peripheral bus clock. + input logic clk, + // Synchronous reset. + input logic rst, + // Peripheral bus interface. + boa_mem_bus.MEM bus, + + // Transmitted data pin. + output logic txd, + // Received data pin. + input logic rxd, + + // UART transmit buffer has emptied. + output logic tx_empty, + // UART receive buffer is no longer empty. + output logic rx_full +); + genvar x; + localparam tx_exp = $clog2(tx_depth) - 1; + localparam rx_exp = $clog2(rx_depth) - 1; + + // Clock divider logic. + logic[dlen-1:0] clk_div = init_div; + always @(posedge clk) begin + if (rst) begin + clk_div <= init_div; + end else if (bus.addr == addr[bus.dlen-1:2]+2 && bus.we == 4'b1111) begin + clk_div <= bus.wdata; + end + end + + // Transmitter logic. + logic tx_trig, tx_fifo_we; + logic tx_ack, tx_fifo_has_dat, tx_fifo_full, tx_busy; + logic[7:0] tx_byte; + // TX FIFO. + param_fifo#(tx_depth, 8, 0) tx_fifo(clk, rst, tx_fifo_we, bus.wdata[7:0], tx_ack, tx_byte, tx_fifo_has_dat, tx_fifo_full); + // Transmitter. + unbuffered_uart_tx#(dlen) tx_phy(clk, rst, clk_div, tx_byte, tx_trig, tx_ack, txd, tx_busy); + assign tx_trig = tx_fifo_has_dat && !tx_ack; + assign tx_fifo_we = bus.addr == addr[bus.dlen-1:2] && bus.we[0]; + + // Receiver logic. + logic rx_fifo_re; + logic rx_trig, rx_fifo_has_dat, rx_fifo_full, rx_busy; + logic[7:0] rx_byte; + logic[7:0] rx_fifo_rdata; + // RX FIFO. + param_fifo#(rx_depth, 8, 1) rx_fifo(clk, rst, rx_trig, rx_byte, rx_fifo_re, rx_fifo_rdata, rx_fifo_has_dat, rx_fifo_full); + // Receiver. + unbuffered_uart_rx#(dlen) rx_phy(clk, rst, clk_div, rx_byte, rx_trig, rx_trig, rxd, rx_busy); + assign rx_fifo_re = bus.addr == addr[bus.dlen-1:2] && bus.re; + + // IRQ logic. + assign tx_empty = !tx_fifo_has_dat; + assign rx_full = rx_fifo_has_dat; + + // Response logic. + logic[31:0] status; + assign status[0] = tx_busy; + assign status[1] = tx_fifo_has_dat; + assign status[2] = !tx_fifo_full; + assign status[15:3] = 0; + assign status[16] = rx_busy; + assign status[17] = rx_fifo_has_dat; + assign status[18] = !rx_fifo_full; + assign status[31:19] = 0; + always @(posedge clk) begin + if (bus.addr == addr[bus.dlen-1:2] && tx_fifo_full && bus.we[0]) begin + // UART write stall. + bus.ready <= !tx_full_stall; + bus.rdata <= rx_fifo_rdata; + + end else if (bus.addr == addr[bus.dlen-1:2] && bus.re) begin + // UART read. + bus.ready <= 1; + bus.rdata <= rx_fifo_rdata; + + end else if (bus.addr == addr[bus.dlen-1:2]+1) begin + // UART status. + bus.ready <= 1; + bus.rdata <= status; + + end else if (bus.addr == addr[bus.dlen-1:2]+2) begin + // UART clock divider. + bus.ready <= 1; + bus.rdata <= clk_div; + + end else begin + // Nothing to wait for. + bus.ready <= 1; + bus.rdata <= 0; + end + end +endmodule diff --git a/dev/hdl/uart.sv b/dev/hdl/mmio/uart.sv.bak similarity index 99% rename from dev/hdl/uart.sv rename to dev/hdl/mmio/uart.sv.bak index e5e2e69..f30150b 100644 --- a/dev/hdl/uart.sv +++ b/dev/hdl/mmio/uart.sv.bak @@ -170,7 +170,7 @@ module boa_peri_uart#( always @(posedge clk) begin if (rst) begin // Reset. - tx_prod <= 0; + tx_prod <= tx_cons; end else if (tx_prod_next != tx_cons && bus.we[0] && bus.addr<<2 == addr) begin // UART write triggered. @@ -183,7 +183,7 @@ module boa_peri_uart#( always @(posedge clk_div[2]) begin if (rst) begin // Reset. - tx_cons <= 0; + // tx_cons <= 0; end else if (tx_trig) begin // TX sent a byte. diff --git a/dev/hdl/mmio/writeable.sv b/dev/hdl/mmio/writeable.sv new file mode 100644 index 0000000..23ef22e --- /dev/null +++ b/dev/hdl/mmio/writeable.sv @@ -0,0 +1,41 @@ + +// Copyright © 2023, Julian Scheffers, see LICENSE for more information + +`timescale 1ns/1ps + + + +// A single word of read-only MMIO. +module boa_peri_writeable#( + // Base address to respond to. + parameter addr = 32'h8000_0000, + // Default value. + parameter def_val = 0 +)( + // CPU clock. + input logic clk, + // Synchronous reset. + input logic rst, + + // Peripheral bus. + boa_mem_bus.MEM bus, + + // Value to present to the bus. + output logic[31:0] value +); + assign bus.ready = 1; + always @(posedge clk) begin + if (rst) begin + value <= def_val; + bus.rdata <= 0; + end else if (bus.addr == addr[bus.alen-1:2]) begin + if (bus.we[0]) value[7:0] <= bus.wdata[7:0]; + if (bus.we[1]) value[15:8] <= bus.wdata[15:8]; + if (bus.we[2]) value[23:16] <= bus.wdata[23:16]; + if (bus.we[3]) value[31:24] <= bus.wdata[31:24]; + bus.rdata <= value; + end else begin + bus.rdata <= 0; + end + end +endmodule diff --git a/dev/hdl/util/fifo.sv b/dev/hdl/util/fifo.sv new file mode 100644 index 0000000..d2c7aef --- /dev/null +++ b/dev/hdl/util/fifo.sv @@ -0,0 +1,65 @@ + +// Copyright © 2023, Julian Scheffers, see LICENSE for more information + +`timescale 1ns/1ps + + + +// Parametric power of two depth FIFO. +module param_fifo#( + // Depth of the FIFO, at least 2. + parameter depth = 2, + // Width of the FIFO. + parameter width = 8, + // Accept writes if full, causing the FIFO to return to empty. + parameter allow_full_we = 1 +)( + // Clock. + input logic clk, + // Synchronous reset. + input logic rst, + + // FIFO write enable. + input logic we, + // FIFO write data. + input logic[width-1:0] wdata, + + // FIFO read enable. + input logic re, + // FIFO read data. + output logic[width-1:0] rdata, + + // FIFO is not empty. + output logic has_dat, + // FIFO is full. + output logic full +); + // FIFO write index. + logic[$clog2(depth)-1:0] wpos; + // FIFO read index. + logic[$clog2(depth)-1:0] rpos; + // FIFO storage. + logic[width-1:0] storage[depth]; + + assign has_dat = rpos != wpos; + assign full = rpos == (wpos+1) % depth; + assign rdata = storage[rpos]; + + always @(posedge clk) begin + if (rst) begin + // Reset. + wpos <= 0; + rpos <= 0; + end else begin + if (re && has_dat) begin + // Consume a byte from the FIFO. + rpos <= rpos + 1; + end + if (we && (!full || allow_full_we)) begin + // Append a byte to the FIFO. + storage[wpos] <= wdata; + wpos <= wpos + 1; + end + end + end +endmodule diff --git a/dev/hdl/util/lfsr128.sv b/dev/hdl/util/lfsr128.sv new file mode 100644 index 0000000..8eeb3fc --- /dev/null +++ b/dev/hdl/util/lfsr128.sv @@ -0,0 +1,23 @@ + +// Copyright © 2023, Julian Scheffers, see LICENSE for more information + +`timescale 1ns/1ps + + + +// A maximal 128-bit LFSR. +module lfsr128#( + parameter [127:0] init_value = 128'h001bb69a_baf65811_caa417d1_19362a08 +)( + input logic clk, + output logic[127:0] state +); + initial begin + state = init_value; + end + // 128-bit left-shifting XNOR LFSR: 128,126,101,99 + always @(posedge clk) begin + state[127:1] <= state[126:0]; + state[0] <= 1 ^ state[127] ^ state[125] ^ state[100] ^ state[98]; + end +endmodule diff --git a/dev/hdl/param_clk_div.sv b/dev/hdl/util/param_clk_div.sv similarity index 100% rename from dev/hdl/param_clk_div.sv rename to dev/hdl/util/param_clk_div.sv diff --git a/fpga/cmod_a7/top.sv b/fpga/cmod_a7/top.sv index b7d2614..1bf2154 100644 --- a/fpga/cmod_a7/top.sv +++ b/fpga/cmod_a7/top.sv @@ -16,7 +16,9 @@ module top( genvar x; `include "boa_fileio.svh" - logic[1:0] rst = 3; + localparam rst_len = 3; + + logic[$clog2(rst_len)-1:0] rst = rst_len; logic shdn = 0; logic txd, rxd; @@ -27,8 +29,6 @@ module top( btnd <= btn; end - logic uart_clk; - param_clk_div#(12000000, 9600*4) uart_div(clk || shdn, uart_clk); logic rtc_clk; param_clk_div#(12000000, 1000000) rtc_div(clk || shdn, rtc_clk); @@ -36,37 +36,48 @@ module top( logic[31:0] gpio_oe; logic[31:0] gpio_in; - // assign gpio_in[31:8] = gpio_out[31:8]; - assign gpio_in[31:0] = gpio_out[31:0]; - // generate - // for (x = 0; x < 8; x = x + 1) begin - // assign pmod[x] = gpio_oe[x] ? gpio_out[x] : 'bz; - // end - // endgenerate + assign gpio_in[10:0] = gpio_out[10:0]; + assign gpio_in[11] = btnd[1]; + assign gpio_in[31:12] = gpio_out[31:12]; assign led_r = !gpio_out[8]; assign led_g = !gpio_out[9]; assign led_b = !gpio_out[10]; - // assign gpio_in[7:0] = pmod; + + logic[127:0] randomness; + logic hyperspeed_clk; + lfsr128 lfsr(hyperspeed_clk, randomness); + // assign hyperspeed_clk = clk; + param_pll#(12000000, 48, 4) rng_pll(clk, hyperspeed_clk); pmu_bus pmb(); - main#(.rom_file({boa_parentdir(`__FILE__), "/../../prog/bootloader/build/rom.mem"})) main(clk || shdn, rtc_clk, rst!=0, uart_clk, txd, rxd, gpio_out, gpio_oe, gpio_in, pmb); + main#( + .rom_file({boa_parentdir(`__FILE__), "/../../prog/bootloader/build/rom.mem"}), + .uart_div(625), + .is_simulator(0) + ) main( + clk || shdn, rtc_clk, rst!=0, + txd, rxd, + gpio_out, gpio_oe, gpio_in, + randomness, + pmb + ); always @(posedge clk) begin if (pmb.shdn) begin shdn <= 1; end if (pmb.rst || btnd[0]) begin - rst <= 3; + rst <= rst_len; shdn <= 0; end else if (rst) begin - rst <= rst - 1; + rst <= rst - 1; end end assign pmod[0] = txd; assign pmod[1] = rxd; - assign pmod[2] = uart_clk; assign pmod[3] = clk; assign pmod[4] = rst; assign pmod[5] = shdn; + assign pmod[6] = hyperspeed_clk; endmodule diff --git a/fpga/xilinx_common/param_pll.sv b/fpga/xilinx_common/param_pll.sv new file mode 100644 index 0000000..abf993d --- /dev/null +++ b/fpga/xilinx_common/param_pll.sv @@ -0,0 +1,42 @@ + +// Copyright © 2023, Julian Scheffers, see LICENSE for more information + +`timescale 1ns/1ps + + + +// Abstract phase locked loop. +// fout = fin * fb_div / out_div +module param_pll#( + // Input clock frequency in hertz. + parameter in_freq = 12000000, + // Division factor for the PLL feedback divider. + parameter fb_div = 5, + // Division factor for the output clock. + parameter out_div = 1 +)( + input logic clk_in, + output logic clk_out +); + logic clk_div; + always @(posedge clk_in) clk_div <= !clk_div; + logic clk_fb; + PLLE2_BASE#( + .CLKIN1_PERIOD(500000000.000 / in_freq), + .CLKFBOUT_MULT(fb_div), + .CLKOUT0_DIVIDE(out_div) + ) pll ( + .CLKFBOUT(clk_fb), + .CLKOUT0(clk_out), + .CLKOUT1(), + .CLKOUT2(), + .CLKOUT3(), + .CLKOUT4(), + .CLKOUT5(), + .LOCKED(), + .CLKFBIN(clk_fb), + .CLKIN1(clk_div), + .PWRDWN(0), + .RST(0) + ); +endmodule diff --git a/fpga/xilinx_common/raw_block_ram.sv b/fpga/xilinx_common/raw_block_ram.sv index e9eb5b4..21e0b8f 100644 --- a/fpga/xilinx_common/raw_block_ram.sv +++ b/fpga/xilinx_common/raw_block_ram.sv @@ -54,6 +54,7 @@ module raw_block_ram#( .regcea(1), .rsta(0), .sbiterra(), + .dbiterra(), .sleep(0), .wea(we) ); @@ -136,7 +137,9 @@ module raw_dp_block_ram#( .rsta(0), .rstb(0), .sbiterra(), + .dbiterra(), .sbiterrb(), + .dbiterrb(), .sleep(0), .wea(a_we), .web(b_we) diff --git a/hdl/boa32_cpu.sv b/hdl/boa32_cpu.sv index 21ebd11..2fb0204 100644 --- a/hdl/boa32_cpu.sv +++ b/hdl/boa32_cpu.sv @@ -140,6 +140,15 @@ module boa32_cpu#( // MEM/WB: Trap cause. logic[3:0] mem_wb_cause; + // WB: Result valid. + logic wb_valid; + // WB: Stores to register RD. + logic wb_use_rd; + // WB: Value to store to register RD. + logic[31:0] wb_rd_val; + // WB: Current instruction word. + logic[31:0] wb_insn; + /* ==== CSR logic ==== */ boa_csr_bus csr(); @@ -359,6 +368,11 @@ module boa32_cpu#( fw_stall_ex |= fw_stall_mem; fw_stall_id |= fw_stall_ex; fw_stall_if |= fw_stall_id; + + fw_stall_mem &= !fw_exception; + fw_stall_ex &= !fw_exception; + fw_stall_id &= !fw_exception; + fw_stall_if &= !fw_exception; end @@ -371,9 +385,9 @@ module boa32_cpu#( // Interrupt latching logic. always @(posedge clk) begin csr_ex.irq_ip[31:16] <= irq[31:16]; - csr_ex.irq_ip[15:4] <= 0; - csr_ex.irq_ip[3] <= mtime_irq; - csr_ex.irq_ip[2:0] <= 0; + csr_ex.irq_ip[15:8] <= 0; + csr_ex.irq_ip[7] <= mtime_irq; + csr_ex.irq_ip[6:0] <= 0; end // Interrupt prioritization logic. @@ -496,10 +510,6 @@ module boa32_cpu#( // Data hazard avoidance. fw_stall_mem, mem_stall_req ); - logic wb_valid; - logic wb_use_rd; - logic[31:0] wb_rd_val; - logic[31:0] wb_insn; always @(posedge clk) begin if (!fw_stall_mem) begin wb_valid <= mem_wb_valid; diff --git a/hdl/modules/boa_arithmetic.sv b/hdl/modules/boa_arithmetic.sv index 042c1cd..0f87262 100644 --- a/hdl/modules/boa_arithmetic.sv +++ b/hdl/modules/boa_arithmetic.sv @@ -192,6 +192,7 @@ module boa_udiv_pipelined#( // Pipelined divider generator. assign div[0] = rhs; assign rem[0] = lhs; + assign res[0] = 0; generate for (x = 0; x < width; x = x + 1) begin boa_div_part#(width-x-1, width, has_reg(x)) part( diff --git a/hdl/stages/boa_stage_if.sv b/hdl/stages/boa_stage_if.sv index 799d475..1d0a38c 100644 --- a/hdl/stages/boa_stage_if.sv +++ b/hdl/stages/boa_stage_if.sv @@ -237,7 +237,11 @@ module boa_stage_if#( assign pbus.we = 0; assign pbus.wdata = 'bx; always @(*) begin - if (!cvalidl) begin + if (rst) begin + // Reset; don't do anything. + pbus.re = 0; + pbus.addr = 'bx; + end else if (!cvalidl) begin // Fetch lower half of instruction. pbus.re = 1; pbus.addr[31:2] = addr[31:2]; @@ -260,7 +264,9 @@ module boa_stage_if#( assign q_pc = addr; assign q_insn = insn; always @(posedge clk) begin - if (!fw_stall_if || rst) begin + if (rst) begin + pc <= entrypoint[31:1]; + end else if (!fw_stall_if) begin pc <= insn_valid ? next_addr : addr; end end diff --git a/prog/Makefile b/prog/Makefile index a24a897..493a117 100644 --- a/prog/Makefile +++ b/prog/Makefile @@ -3,12 +3,18 @@ all: make -C test all + make -C gpiotest all + make -C uarttest all make -C bootloader all build: make -C test build + make -C gpiotest build + make -C uarttest build make -C bootloader build clean: make -C test clean + make -C gpiotest clean + make -C uarttest clean make -C bootloader clean diff --git a/prog/bootloader/README.md b/prog/bootloader/README.md index d63226d..2c4c58f 100644 --- a/prog/bootloader/README.md +++ b/prog/bootloader/README.md @@ -32,6 +32,7 @@ The following is the basic layout of a packet: | P_ACK | 0x02 | Request acknowledgement | P_WHO | 0x03 | Identity request | P_IDENT | 0x04 | Identity response +| P_SPEED | 0x05 | UART baudrate setting | P_WRITE | 0x10 | Prepare for a memory write | P_READ | 0x11 | Request a memory read | P_WDATA | 0x12 | Data associated with P_WRITE @@ -64,7 +65,8 @@ Acknowledgement of a previous packet, positive or negative, from either side of ### Data field format | length | value | description | :----- | :------- | :---------- -| 1 | status | Acknowledgement status +| 4 | status | Acknowledgement status +| 4 | cause | Error cause ### Acknowledgement status values | name | value | description @@ -76,6 +78,8 @@ Acknowledgement of a previous packet, positive or negative, from either side of | A_ADDR | 0x04 | The address range is not supported | A_RDONLY | 0x05 | The address range is read-only | A_NOEXEC | 0x06 | The address range is not executable +| A_TIME | 0x07 | Communication timeout +| A_NSPEED | 0x08 | Unsupported speed ## Packet: P_WHO @@ -87,6 +91,15 @@ ASCII string representing the computer. Format TBD. +## Packet: P_SPEED +Set the UART baud rate to a new value. + +### Data field format +| length | value | description +| :----- | :------- | :---------- +| 4 | speed | Desired baud rate + + ## Packet: P_WRITE Request to write to physical memory. The computer will send an acknowledgement in response, after which a P_WDATA packet may be sent. diff --git a/prog/bootloader/include/protocol.h b/prog/bootloader/include/protocol.h index 3588070..928fd96 100644 --- a/prog/bootloader/include/protocol.h +++ b/prog/bootloader/include/protocol.h @@ -21,6 +21,8 @@ #define P_WHO 0x03 // Identity response. #define P_IDENT 0x04 +// UART baudrate setting. +#define P_SPEED 0x05 // Prepare for a memory write. #define P_WRITE 0x10 // Request a memory read. @@ -48,9 +50,13 @@ #define A_RDONLY 0x05 // The address range is not executable. #define A_NOEXEC 0x06 +// Communication timeout. +#define A_TIME 0x07 +// Unsupported speed. +#define A_NSPEED 0x08 // Packet header structure. -typedef struct { +typedef struct __attribute__((packed)) { // Describes the request or data stored in this packet. uint32_t type; // Length of the remaining data. @@ -58,21 +64,27 @@ typedef struct { } phdr_t; // P_PING and P_PONG data format. -typedef struct { +typedef struct __attribute__((packed)) { // Arbitrary data. uint8_t nonce[16]; } p_ping_t; // P_ACK data format. -typedef struct { +typedef struct __attribute__((packed)) { // Acknowledgement type. - uint8_t ack_type; + uint32_t ack_type; // Cause of potential errors. uint32_t cause; } p_ack_t; +// P_SPEED data format. +typedef struct __attribute__((packed)) { + // Desired UART baud rate. + uint32_t speed; +} p_speed_t; + // P_WRITE data format. -typedef struct { +typedef struct __attribute__((packed)) { // Base address to write to. uint32_t addr; // Length to write. @@ -80,7 +92,7 @@ typedef struct { } p_write_t; // P_READ data format. -typedef struct { +typedef struct __attribute__((packed)) { // Base address to read from. uint32_t addr; // Length to read. @@ -88,13 +100,13 @@ typedef struct { } p_read_t; // P_JUMP data format. -typedef struct { +typedef struct __attribute__((packed)) { // Address to jump to. uint32_t addr; } p_jump_t; // P_CALL data format. -typedef struct { +typedef struct __attribute__((packed)) { // Address to call. uint32_t addr; } p_call_t; @@ -103,6 +115,7 @@ typedef struct { typedef union { p_ping_t p_ping; p_ack_t p_ack; + p_speed_t p_speed; p_write_t p_write; p_read_t p_read; p_jump_t p_jump; diff --git a/prog/bootloader/src/main.c b/prog/bootloader/src/main.c index 6ef7231..5ca9df8 100644 --- a/prog/bootloader/src/main.c +++ b/prog/bootloader/src/main.c @@ -2,6 +2,7 @@ // Copyright © 2023, Julian Scheffers, see LICENSE for more information #include "gpio.h" +#include "is_simulator.h" #include "mtime.h" #include "print.h" #include "protocol.h" @@ -130,6 +131,28 @@ void p_who() { send_packet(&header, ident); } +// Handle a P_SPEED packet. +void p_speed() { + if (header.length != sizeof(p_speed_t)) { + send_ack(A_NCAP); + return; + } + + // Determine appropriate divider. + uint32_t divider = UART_BASE_FREQ / data.p_speed.speed; + if (divider < 4 || divider > 65535) { + send_ack(A_NSPEED); + return; + } else { + send_ack(A_ACK); + } + + // Wait for UART to finish sending. + while (UART0.status.tx_busy || UART0.status.rx_hasdat); + // Configure new frequency. + UART0.clk_div = divider; +} + // Handle a P_WRITE packet. void p_write() { if (header.length != sizeof(data.p_write)) { @@ -233,6 +256,7 @@ void handle_rx(uint8_t rxd) { switch (header.type) { case P_PING: p_ping(); break; case P_WHO: p_who(); break; + case P_SPEED: p_speed(); break; case P_WRITE: p_write(); break; case P_READ: p_read(); break; case P_WDATA: p_wdata(); break; @@ -265,6 +289,16 @@ void isr() { // Does stuff? void main() { + // Blink the LED red at startup. + if (!IS_SIMULATOR) { + mtime = 0; + GPIO.oe = 1 << 8; + GPIO.port = 1 << 8; + while (mtime < 100000); + GPIO.oe = 0; + GPIO.port = 0; + } + while (1) { if (UART0.status.rx_hasdat) { handle_rx(UART0.fifo); diff --git a/prog/common/CMakeLists.txt b/prog/common/CMakeLists.txt index e6d8a27..043aa18 100644 --- a/prog/common/CMakeLists.txt +++ b/prog/common/CMakeLists.txt @@ -15,11 +15,14 @@ if(NOT DEFINED linkerscript) set(linkerscript ${CMAKE_CURRENT_LIST_DIR}/ld/linker_${memory_layout}.ld) endif() -if(${memory_layout} EQUAL "ram") - target_compile_definitions(${target} PUBLIC - -DROM_PROGRAM - ) -endif() +target_compile_definitions(${target} PUBLIC + -Dmemory_layout_${memory_layout} +) + +target_compile_definitions(${target} PUBLIC + -DCPU_FREQ=12000000 + -DUART_BASE_FREQ=12000000 +) target_compile_options(${target} PUBLIC -ffreestanding -nostdlib -nodefaultlibs -nostdinc -O2 diff --git a/prog/common/asm/start.S b/prog/common/asm/start.S index 5b4cc1a..c206ebc 100644 --- a/prog/common/asm/start.S +++ b/prog/common/asm/start.S @@ -37,7 +37,7 @@ _start_bss_loop: blt a0, a1, _start_bss_loop _stop_bss_loop: -#ifdef ROM_PROGRAM +#if defined(memory_layout_rom) || defined(memory_layout_bootloader) # Initialize DATA. la a0, __start_data la a1, __start_data_rom @@ -83,6 +83,22 @@ _halt: + .type reset, %function + .global reset +reset: + # Wait for UART0 to finish sending. + lw a0, __uart0_base+4 + andi a0, a0, 3 + bgt a0, x0, reset + + # Send PMU reset command. + li a0, 1 + sw a0, __pmu_base, t0 +_reset: + j _reset + + + .section ".bss" .align 4 .global __stack_size diff --git a/prog/common/include/gpio.h b/prog/common/include/gpio.h index f304e1a..bd0c07e 100644 --- a/prog/common/include/gpio.h +++ b/prog/common/include/gpio.h @@ -20,14 +20,26 @@ typedef struct { // GPIO peripheral. typedef struct { // Pin I/O. - volatile uint32_t port; + uint32_t volatile port; // Pin output enable. - volatile uint32_t oe; + uint32_t volatile oe; // Padding. - volatile uint32_t _padding[30]; + uint32_t volatile _padding[30]; // Pin configuration. - volatile gpio_pin_t cfg[32]; + gpio_pin_t volatile cfg[32]; } gpio_t; +// PWM peripheral. +typedef struct { + // PWM value. + uint8_t volatile val; + // PWM clock divider. + uint8_t volatile div; + // Padding. + uint32_t volatile _padding[3]; +} pwm_t; + // GPIO address. extern gpio_t GPIO asm("__gpio_base"); +// PWM address. +extern pwm_t PWM[8] asm("__pwm_base"); diff --git a/prog/common/include/is_simulator.h b/prog/common/include/is_simulator.h new file mode 100644 index 0000000..6f31211 --- /dev/null +++ b/prog/common/include/is_simulator.h @@ -0,0 +1,11 @@ + +// Copyright © 2023, Julian Scheffers, see LICENSE for more information + +#pragma once + +#include +#include +#include + +// Is simulator register address. +extern uint32_t const IS_SIMULATOR asm("__is_simulator_base"); diff --git a/prog/common/include/rng.h b/prog/common/include/rng.h new file mode 100644 index 0000000..d85d0d2 --- /dev/null +++ b/prog/common/include/rng.h @@ -0,0 +1,11 @@ + +// Copyright © 2023, Julian Scheffers, see LICENSE for more information + +#pragma once + +#include +#include +#include + +// Hardware random number generator address. +extern uint32_t volatile RNG asm("__rng_base"); diff --git a/prog/common/include/uart.h b/prog/common/include/uart.h index 003934a..e956c5e 100644 --- a/prog/common/include/uart.h +++ b/prog/common/include/uart.h @@ -7,6 +7,9 @@ #include #include +#define UART_TX_EMPTY_IRQ 16 +#define UART_RX_FULL_IRQ 17 + // UART status register. typedef struct { // Transmitter is currently sending. @@ -30,11 +33,13 @@ typedef struct { // UART peripheral. typedef struct { // Write to send data, read to receive. - volatile uint8_t fifo; + uint8_t volatile fifo; // Padding. - volatile uint8_t _padding0[3]; + uint8_t volatile _padding0[3]; // Status register. - volatile uart_status_t status; + uart_status_t volatile status; + // Clock divider setting. + uint32_t volatile clk_div; } uart_t; // UART 0 address. diff --git a/prog/common/ld/memory_layout.ld b/prog/common/ld/memory_layout.ld index 9fb2969..6f8f6da 100644 --- a/prog/common/ld/memory_layout.ld +++ b/prog/common/ld/memory_layout.ld @@ -11,6 +11,10 @@ __start_cpummio = 0xff000000; __mtime = __start_cpummio; __mtimecmp = __start_cpummio + 8; +__is_simulator_base = __start_peri + 0xffc; + __uart0_base = __start_peri + 0x000; __pmu_base = __start_peri + 0x100; __gpio_base = __start_peri + 0x200; +__rng_base = __start_peri + 0x300; +__pwm_base = __start_peri + 0x480; diff --git a/prog/gpiotest/CMakeLists.txt b/prog/gpiotest/CMakeLists.txt new file mode 100644 index 0000000..164971a --- /dev/null +++ b/prog/gpiotest/CMakeLists.txt @@ -0,0 +1,16 @@ + +# Copyright © 2023, Julian Scheffers, see LICENSE for more information + +cmake_minimum_required(VERSION 3.10.0) + +set(CMAKE_C_COMPILER "riscv32-unknown-elf-gcc") +set(CMAKE_EXPORT_COMPILE_COMMANDS true) + +project(axo_test_rom C ASM) +set(target rom.elf) + +add_executable(${target} + src/main.c +) + +include(${CMAKE_CURRENT_LIST_DIR}/../common/CMakeLists.txt) diff --git a/prog/gpiotest/Makefile b/prog/gpiotest/Makefile new file mode 100644 index 0000000..793a217 --- /dev/null +++ b/prog/gpiotest/Makefile @@ -0,0 +1,16 @@ + +MAKEFLAGS += --silent --no-print-directory + +.PHONY: all build clean + +all: build + +build: + mkdir -p build + cmake -B build + cmake --build build + riscv32-unknown-elf-objcopy -O binary build/rom.elf build/rom.bin + ../../tools/bin2mem.py build/rom.bin build/rom.mem 32 + +clean: + rm -rf build diff --git a/prog/gpiotest/src/main.c b/prog/gpiotest/src/main.c new file mode 100644 index 0000000..06b07f9 --- /dev/null +++ b/prog/gpiotest/src/main.c @@ -0,0 +1,103 @@ + +// Copyright © 2023, Julian Scheffers, see LICENSE for more information + +#include "gpio.h" +#include "mtime.h" +#include "print.h" +#include "rng.h" +#include "uart.h" + +#include +#include +#include + +#include + +// Delay of 1/256 increment of PWM in microseconds. +#define PWM_DELAY 10 + +extern void halt(); +extern void reset(); + +uint8_t volatile pwm_r = 0; +uint8_t volatile pwm_g = 0; +uint8_t volatile pwm_b = 0; + +void handle_mtime() { + static uint8_t pwm_state = 1; + + // Update GPIO pins. + uint32_t cur = GPIO.port; + pwm_state++; + if (pwm_state == 0) { + cur |= (0b111 << 8); + } + if (pwm_r == pwm_state) + cur &= ~(1 << 8); + if (pwm_g == pwm_state) + cur &= ~(1 << 9); + if (pwm_b == pwm_state) + cur &= ~(1 << 10); + GPIO.port = cur; + + // Update timer. + mtimecmp += PWM_DELAY; +} + +void isr() { + long mcause; + asm("csrr %0, mcause" : "=r"(mcause)); + if (mcause < 0) { + // Interrupt. + mcause &= 31; + // Unhandled interrupt. + print("Interrupt "); + putd(mcause, 2); + print("\n"); + // halt(); + + } else { + // Trap. + print("Trap "); + putd(mcause, 2); + print("\n"); + // halt(); + } +} + +static inline uint64_t time_us() { + return mtime; +} + +static void delay(uint64_t us) { + uint64_t limit = time_us() + us; + while (time_us() < limit); +} + +void main() { + // Set mtime to 0. + mtime = 0; + + // Configure the LEDs to PWM signals. + GPIO.cfg[8] = (gpio_pin_t){.ext = true, .signal = 0}; + GPIO.cfg[9] = (gpio_pin_t){.ext = true, .signal = 1}; + GPIO.cfg[10] = (gpio_pin_t){.ext = true, .signal = 2}; + + while (1) { + bool r = RNG & 1; + bool g = RNG & 2; + bool b = RNG & 4; + for (int i = 0; i < 256; i++) { + PWM[0].val = r * i; + PWM[1].val = g * i; + PWM[2].val = b * i; + delay(2000); + } + for (int i = 255; i >= 0; i--) { + PWM[0].val = r * i; + PWM[1].val = g * i; + PWM[2].val = b * i; + delay(2000); + } + } +} diff --git a/prog/test/src/main.c b/prog/test/src/main.c index fe6fd52..04d6cf1 100644 --- a/prog/test/src/main.c +++ b/prog/test/src/main.c @@ -2,6 +2,7 @@ // Copyright © 2023, Julian Scheffers, see LICENSE for more information #include "print.h" +#include "rng.h" #include "uart.h" #include @@ -21,6 +22,8 @@ void isr() { UART0.fifo = c; if (c == '\n') { done = true; + } else if (rxlen == sizeof(rxbuf) - 1) { + done = true; } else { rxbuf[rxlen++] = c; } @@ -32,11 +35,26 @@ void main() { asm("csrsi mstatus, 8"); while (!done); done = false; + print("RNG test:\n"); + putx(RNG, 8); + putc('\n'); + putx(RNG, 8); + putc('\n'); + putx(RNG, 8); + putc('\n'); + putx(RNG, 8); + putc('\n'); + putx(RNG, 8); + putc('\n'); print("Hello, what's your name?\n> "); while (!done); print("Hello, "); print((char const *)rxbuf); print("!\nYour name is "); - putd(strlen((char const *)rxbuf), 3); + int len = strlen((char const *)rxbuf); + putd(len, 3); print(" bytes long!\n"); + if (len > 100) { + print("That's a long name!\n"); + } } diff --git a/prog/uarttest/CMakeLists.txt b/prog/uarttest/CMakeLists.txt new file mode 100644 index 0000000..164971a --- /dev/null +++ b/prog/uarttest/CMakeLists.txt @@ -0,0 +1,16 @@ + +# Copyright © 2023, Julian Scheffers, see LICENSE for more information + +cmake_minimum_required(VERSION 3.10.0) + +set(CMAKE_C_COMPILER "riscv32-unknown-elf-gcc") +set(CMAKE_EXPORT_COMPILE_COMMANDS true) + +project(axo_test_rom C ASM) +set(target rom.elf) + +add_executable(${target} + src/main.c +) + +include(${CMAKE_CURRENT_LIST_DIR}/../common/CMakeLists.txt) diff --git a/prog/uarttest/Makefile b/prog/uarttest/Makefile new file mode 100644 index 0000000..793a217 --- /dev/null +++ b/prog/uarttest/Makefile @@ -0,0 +1,16 @@ + +MAKEFLAGS += --silent --no-print-directory + +.PHONY: all build clean + +all: build + +build: + mkdir -p build + cmake -B build + cmake --build build + riscv32-unknown-elf-objcopy -O binary build/rom.elf build/rom.bin + ../../tools/bin2mem.py build/rom.bin build/rom.mem 32 + +clean: + rm -rf build diff --git a/prog/uarttest/src/main.c b/prog/uarttest/src/main.c new file mode 100644 index 0000000..4e31c9f --- /dev/null +++ b/prog/uarttest/src/main.c @@ -0,0 +1,45 @@ + +// Copyright © 2023, Julian Scheffers, see LICENSE for more information + +#include "print.h" +#include "rng.h" +#include "uart.h" + +#include +#include +#include + +#include + +extern void halt(); + +void isr() { + long mcause; + asm("csrr %0, mcause" : "=r"(mcause)); + if (mcause < 0) { + // Interrupt. + mcause &= 31; + // Unhandled interrupt. + print("Interrupt "); + putd(mcause, 2); + print("\n"); + // halt(); + + } else { + // Trap. + print("Trap "); + putd(mcause, 2); + print("\n"); + // halt(); + } +} + +void main() { + // Set UART to 115200 baud. + UART0.clk_div = 104; + // Wait for a UART receive. + while (!UART0.status.rx_hasdat); + // Print a funny message. + print("Hello, World at 115200 baud! I'm making this message extra long just to make sure it all gets received " + "properly.\n"); +} diff --git a/sim/dev/hdl/top.sv b/sim/dev/hdl/top.sv index 7302ee4..045560c 100644 --- a/sim/dev/hdl/top.sv +++ b/sim/dev/hdl/top.sv @@ -12,15 +12,28 @@ module top( ); `include "boa_fileio.svh" logic[1:0] rst = 3; - logic uart_clk; + logic rtc_clk; logic[31:0] gpio_out; logic[31:0] gpio_oe; logic[31:0] gpio_in; assign gpio_in = gpio_out; - param_clk_div#(1, 1) clk_div(clk, uart_clk); + param_clk_div#(10, 1) rtc_div(clk, rtc_clk); pmu_bus pmb(); - main#(.rom_file({boa_parentdir(`__FILE__), "/../obj_dir/rom.mem"}), .uart_buf(8192)) main(clk, clk, rst!=0, uart_clk, tx, rx, gpio_out, gpio_oe, gpio_in, pmb); + logic[31:0] randomness = $urandom(); + main#( + .rom_file({boa_parentdir(`__FILE__), "/../obj_dir/rom.mem"}), + .uart_buf(8192), + .uart_div(4), + .is_simulator(1) + ) main ( + clk, rtc_clk, rst!=0, + tx, rx, + gpio_out, gpio_oe, gpio_in, + randomness, + pmb + ); always @(posedge clk) begin + randomness <= $urandom(); if (pmb.shdn) begin $display("PMU poweroff"); $finish; end if (pmb.rst) rst <= 3; else if (rst) rst <= rst - 1; diff --git a/tools/programmer/src/main.c b/tools/programmer/src/main.c index 0ee10b5..2a0186c 100644 --- a/tools/programmer/src/main.c +++ b/tools/programmer/src/main.c @@ -27,6 +27,9 @@ // Currently waiting; too much data. #define RX_NCAP 5 +// Show raw transmissions in hexadecimal. +bool show_hex; + // Currently receiving. int rx_type; // Receive count. @@ -55,20 +58,28 @@ void handle_packet(); // Send a packet to a different stream. void send_packet1(FILE *fd, phdr_t const *header, void const *data) { fputc(2, fd); + if (show_hex) + printf("> 02"); uint8_t xsum = 2; uint8_t const *tx_ptr = (uint8_t const *)header; fwrite(tx_ptr, 1, sizeof(phdr_t), fd); for (size_t i = 0; i < sizeof(phdr_t); i++) { + if (show_hex) + printf(" %02x", tx_ptr[i]); xsum += tx_ptr[i]; } tx_ptr = (uint8_t const *)data; fwrite(tx_ptr, 1, header->length, fd); for (size_t i = 0; i < header->length; i++) { + if (show_hex) + printf(" %02x", tx_ptr[i]); xsum += tx_ptr[i]; } + if (show_hex) + printf(" %02x\n", xsum); fputc(xsum, fd); fflush(fd); } @@ -80,12 +91,21 @@ void send_packet(phdr_t const *header, void const *data) { // Handle a received byte. void handle_rx(uint8_t rxd) { + if (rx_type == RX_NONE && show_hex) { + printf("<"); + } + if (show_hex) { + printf(" %02x", rxd); + fflush(stdout); + } if (rx_type == RX_NONE) { xsum = rxd; if (rxd == 2) { rx_len = 0; rx_type = RX_PHDR; rx_ptr = (uint8_t *)&header; + } else if (show_hex) { + printf("\n"); } } else if (rx_type == RX_PHDR) { rx_ptr[rx_len++] = rxd; @@ -113,6 +133,8 @@ void handle_rx(uint8_t rxd) { rx_type = RX_XSUM; } else if (rx_type == RX_XSUM) { rx_xsum = rxd; + if (show_hex) + printf("\n"); if (xsum != rxd) { handle_xsum(); } else if (header.length > sizeof(data)) { @@ -182,7 +204,7 @@ bool await_packet(phdr_t const *phdr, void const *pdat) { data.p_ack.cause & 255 ); } else { - return true; + return phdr->type != P_ACK || ((p_ack_t *)pdat)->ack_type == A_ACK; } } try++; @@ -191,6 +213,78 @@ bool await_packet(phdr_t const *phdr, void const *pdat) { +// Try to ping the computer. +bool ping() { + phdr_t phdr = { + .type = P_PING, + .length = sizeof(p_ping_t), + }; + p_ping_t pdat; + + // Attempt to put random date in the ping. + FILE *fd = fopen("/dev/random", "rb"); + if (fd) + fread(&pdat, 1, sizeof(pdat), fd); + else + memset(&pdat, 0xcc, sizeof(pdat)); + + // Send the ping packet. + if (!await_packet(&phdr, &pdat)) { + return false; + } + if (memcmp(&pdat, &data.p_ping, sizeof(pdat))) { + printf("Ping payload mismatch.\n"); + return false; + } else { + return true; + } +} + +// Try to change the UART speed. +bool change_speed(int new_speed) { + // Speed change packet. + phdr_t phdr = { + .type = P_SPEED, + .length = sizeof(p_speed_t), + }; + p_speed_t pdat = { + .speed = new_speed, + }; + + // Request speed change. + if (!await_packet(&phdr, &pdat)) { + return false; + } + if (expect_ack(A_NSPEED)) { + printf("Speed %d unsupported\n", new_speed); + return false; + } else if (!expect_ack(A_ACK)) { + printf("Speed change unsupported\n"); + return false; + } + + // Upon ACK, change serial port speed. + fflush(uart); + struct termios new_term; + tcgetattr(fileno(uart), &new_term); + cfsetispeed(&new_term, new_speed); + cfsetospeed(&new_term, new_speed); + tcsetattr(fileno(uart), TCSANOW, &new_term); + + // Wait around for just a moment to let everyone catch up. + usleep(10000); + + // If a ping succeeds the baudrate change was successful. + if (ping()) { + printf("Speed changed to %d\n", new_speed); + return true; + } else { + return false; + } +} + + + // Upload an ELF file to the thing. bool upload_elf(char const *filename, bool run) { // Open ELF file. @@ -319,6 +413,7 @@ void get_help(int argc, char **argv) { printf(" %s upload \n", id); printf(" %s run \n", id); printf(" %s id\n", id); + printf(" %s ping\n", id); printf(" %s jump
\n", id); printf(" %s call
\n", id); exit(1); @@ -349,23 +444,36 @@ int main(int argc, char **argv) { printf("Failed to open %s\n", argv[1]); } + show_hex = getenv("SHOW_HEX"); + // Set UART to nonblocking. orig_flags = fcntl(0, F_GETFL); fcntl(fileno(uart), F_SETFL, orig_flags | O_NONBLOCK); // Set TTY to character break. tcgetattr(fileno(uart), &orig_term); struct termios new_term = orig_term; + cfsetispeed(&new_term, 19200); + cfsetospeed(&new_term, 19200); cfmakeraw(&new_term); - // new_term.c_oflag = 0; - // new_term.c_iflag = 0; - // new_term.c_lflag = 0; - // new_term.c_cflag = CLOCAL | CREAD | CS8; tcsetattr(fileno(uart), TCSANOW, &new_term); + // Configure speed. + char *speed_str = getenv("BOAPROG_SPEED"); + if (speed_str) { + int speed = atoi(speed_str); + if (speed > 0) { + change_speed(speed); + } else { + printf("Ignoring invalid speed %s\n", speed_str); + } + } + if (argc == 4 && !strcmp(argv[2], "upload")) { return !upload_elf(argv[3], false); } else if (argc == 4 && !strcmp(argv[2], "run")) { return !upload_elf(argv[3], true); + } else if (argc == 3 && !strcmp(argv[2], "ping")) { + return !ping(); } else if (argc == 3 && !strcmp(argv[2], "id")) { return !get_id(); } else if (argc == 4 && !strcmp(argv[2], "jump")) {