summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--Makefile37
-rw-r--r--bin2bcd.sv98
-rw-r--r--fibseq.sv30
-rw-r--r--init.tcl18
-rw-r--r--jtag_uart.hex6
-rw-r--r--jtag_uart.sv82
-rw-r--r--main.cpp56
-rw-r--r--ntoa.sv83
-rw-r--r--rom.hex18
-rw-r--r--top.sv145
11 files changed, 574 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
/build
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..d270ffb
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,37 @@
1VERILOG := $(wildcard **.sv)
2HEADERS := $(wildcard **.svh)
3CPPSRCS := $(wildcard **.cpp)
4COLLATERAL := $(wildcard **.hex)
5
6QUARTUS ?= /opt/quartus-lite/20.1.1.720/
7
8QUARTUS_SH := $(QUARTUS)/quartus/bin/quartus_sh
9
10sim: build/Vtop
11 $<
12.PHONY: sim
13
14gui: build/toycpu.qpf
15 cd build; $(QUARTUS_SH) --gui toycpu
16.PHONY: gui
17
18qpf: build/toycpu.qpf
19.PHONY: qpf
20
21pof: build/toycpu.pof
22.PHONY: pof
23
24build/Vtop: $(VERILOG) $(HEADERS) $(CPPSRCS) $(COLLATERAL)
25 @mkdir -p build
26 verilator +1800-2017ext+sv -Wall -Wno-BLKSEQ -O3 --Mdir build --trace --cc --build --exe --top-module top $(VERILOG) $(CPPSRCS)
27
28build/toycpu.pof: build/toycpu.qpf $(VERILOG) $(COLLATERAL)
29 cd build; $(QUARTUS_SH) --flow compile toycpu
30
31build/toycpu.qpf: init.tcl
32 @mkdir -p build
33 cd build; $(QUARTUS_SH) -t ../$< $(addprefix $(PWD),$(VERILOG))
34
35clean:
36 rm -rf build
37.PHONY: clean
diff --git a/bin2bcd.sv b/bin2bcd.sv
new file mode 100644
index 0000000..80dc633
--- /dev/null
+++ b/bin2bcd.sv
@@ -0,0 +1,98 @@
1module bin2bcd
2 #( BITS = 8
3 , BASE = 10
4 )
5 ( input bit clk
6 , input bit reset
7
8 , output bit bin_ready
9 , input bit bin_valid `define bin_valid $past(bin_valid)
10 , input bit [BITS-1:0] bin_data `define bin_data $past(bin_data)
11
12 , input bit bcd_ready `define bcd_ready $past(bcd_ready)
13 , output bit bcd_valid
14 , output bit [DIGITS-1:0][BASE_BITS-1:0] bcd_data
15 );
16
17// FIXME I don't think this works for odd bases
18
19localparam BASE_BITS = $clog2(BASE);
20localparam SLACK = (1 << BASE_BITS) - BASE;
21localparam DIGITS = $rtoi($ceil($ln(1 << BITS) / $ln(BASE)));
22localparam CARRY_TEST = $rtoi($ceil($itor(BASE) / 2));
23localparam CARRY_ADD = $rtoi($ceil($itor(SLACK) / 2));
24
25`ifdef DEBUG_BIN2BCD
26
27initial $display("%d BITS", BITS);
28initial $display("%d BASE", BASE);
29initial $display("%d BASE_BITS", BASE_BITS);
30initial $display("%d SLACK", SLACK);
31initial $display("%d DIGITS", DIGITS);
32initial $display("%d CARRY_TEST", CARRY_TEST);
33initial $display("%d CARRY_ADD", CARRY_ADD);
34
35initial for(int i = 0; i < BASE; i = i + 1) begin
36 // verilator lint_off WIDTH
37 automatic bit [BASE_BITS-1:0] n = i;
38 automatic bit [BASE_BITS-1:0] a = n >= CARRY_TEST ? CARRY_ADD : 0;
39 automatic bit [BASE_BITS-1:0] s = n + a;
40 // verilator lint_on WIDTH
41 automatic bit c;
42 automatic bit [BASE_BITS-1:0] d;
43 {c, d} = {s, 1'b0};
44 $display("\t\t(%x + %x => %x) * 2 => %x:%x", n, a, s, c, d);
45end
46
47`endif
48
49bit bin_b_valid;
50bit [BITS-1:0] bin_b_data;
51
52bit [DIGITS-1:0][BASE_BITS-1:0] bcd;;
53
54bit [$clog2(BITS):0] work;
55
56always_ff @(posedge clk) begin
57 if (reset) begin
58 bin_ready = 0;
59 bcd_valid = 0;
60 bin_b_valid = 0;
61 end else begin
62 if (bin_ready && `bin_valid) begin
63 bin_b_valid = 1;
64 bin_b_data = `bin_data;
65 bcd = 0;
66 work = BITS;
67 for (int i = BITS; i > 0; i = i - 1) begin
68 if (bin_b_data[BITS-1]) break;
69 bin_b_data = { bin_b_data[BITS-2:0], 1'b0 };
70 work = work - 1;
71 end
72 end
73
74 if (bin_b_valid && work != 0) begin
75 for (int i = 0; i < DIGITS; i = i + 1)
76 // verilator lint_off WIDTH
77 if (bcd[i] >= CARRY_TEST) bcd[i] = bcd[i] + CARRY_ADD;
78 // verilator lint_on WIDTH
79 for (int i = DIGITS - 1; i > 0; i = i - 1)
80 bcd[i] = { bcd[i][BASE_BITS-2:0], bcd[i-1][BASE_BITS-1] };
81 bcd[0] = { bcd[0][BASE_BITS-2:0], bin_b_data[BITS-1] };
82 bin_b_data = { bin_b_data[BITS-2:0], 1'b0 };
83
84 work = work - 1;
85 end
86
87 if (`bcd_ready) bcd_valid = 0;
88 if (!bcd_valid && bin_b_valid && work == 0) begin
89 bcd_valid = 1;
90 bcd_data = bcd;
91 bin_b_valid = 0;
92 end
93
94 bin_ready = !bin_b_valid;
95 end
96end
97
98endmodule
diff --git a/fibseq.sv b/fibseq.sv
new file mode 100644
index 0000000..f877b98
--- /dev/null
+++ b/fibseq.sv
@@ -0,0 +1,30 @@
1module fibseq
2 #( BITS = 8
3 )
4 ( input bit clk
5 , input bit reset
6
7 , input bit ready `define ready $past(ready)
8 , output bit valid
9 , output bit [BITS-1:0] data
10 );
11
12bit [BITS-1:0] a;
13bit [BITS-1:0] b;
14
15always_ff @(posedge clk) begin
16 if (reset) begin
17 valid = 0;
18 a = 0;
19 b = 1;
20 end else begin
21 if (`ready) valid = 0;
22 if (!valid) begin
23 valid = 1;
24 data = a;
25 {a, b} = {b, a + b};
26 end
27 end
28end
29
30endmodule
diff --git a/init.tcl b/init.tcl
new file mode 100644
index 0000000..66cea3f
--- /dev/null
+++ b/init.tcl
@@ -0,0 +1,18 @@
1global quartus
2
3project_new toycpu -revision toycpu -overwrite
4
5set_global_assignment -name DEVICE 10CL025YU256I7G
6
7set_global_assignment -name TOP_LEVEL_ENTITY top
8
9set_location_assignment -to clk PIN_E1
10set_location_assignment -to reset_n PIN_J15
11
12create_base_clock -fmax "50 MHz" -target clk clock_50m
13
14foreach arg $quartus(args) {
15 set_global_assignment -name VERILOG_FILE $arg
16}
17
18project_close
diff --git a/jtag_uart.hex b/jtag_uart.hex
new file mode 100644
index 0000000..504999f
--- /dev/null
+++ b/jtag_uart.hex
@@ -0,0 +1,6 @@
1@0
2
3// "Hello, FPGA!\n"
448 65 6c 6c 6f 2c 20 46 50 47 41 21 0a
5
600
diff --git a/jtag_uart.sv b/jtag_uart.sv
new file mode 100644
index 0000000..ddb5ecb
--- /dev/null
+++ b/jtag_uart.sv
@@ -0,0 +1,82 @@
1module jtag_uart
2 #( INSTANCE = 0
3
4 , RX_FIFO_BITS = 6
5 , TX_FIFO_BITS = 6
6 )
7 ( input bit clk
8 , input bit reset
9
10 , input bit rx_ready `define rx_ready $past(rx_ready)
11 , output bit rx_valid
12 , output bit [7:0] rx_data
13
14 , output bit tx_ready
15 , input bit tx_valid `define tx_valid $past(tx_valid)
16 , input bit [7:0] tx_data `define tx_data $past(tx_data)
17 );
18
19`ifdef SYNTHESIS
20
21alt_jtag_atlantic
22 #( .INSTANCE_ID(INSTANCE)
23 , .LOG2_RXFIFO_DEPTH(RX_FIFO_BITS)
24 , .LOG2_TXFIFO_DEPTH(TX_FIFO_BITS)
25 , .SLD_AUTO_INSTANCE_INDEX("NO")
26 ) real_jtag
27 ( .clk(clk)
28 , .rst_n(!reset)
29 , .r_dat(tx_data)
30 , .r_val(tx_valid)
31 , .r_ena(tx_ready)
32 , .t_dat(rx_data)
33 , .t_dav(rx_ready)
34 , .t_ena(rx_valid)
35 );
36
37`else
38
39bit [7:0] sim_rx_rom [0:(1<<16)-1];
40initial $readmemh("jtag_uart.hex", sim_rx_rom);
41
42bit [15:0] sim_rx_addr;
43
44bit tx_b_valid;
45bit [7:0] tx_b_data;
46
47always_ff @(posedge clk) begin
48 if (reset) begin
49 rx_valid = 0;
50 tx_ready = 0;
51 sim_rx_addr = 0;
52 tx_b_valid = 0;
53 end else begin
54 automatic bit [7:0] sim_rx_data = sim_rx_rom[sim_rx_addr];
55
56 // RX logic
57 if (`rx_ready) rx_valid = 0;
58 if (!rx_valid && (sim_rx_data != 0)) begin
59`ifdef JTAG_UART_LOCAL_ECHO
60 $write("%s", sim_rx_data);
61`endif
62 rx_valid = 1;
63 rx_data = sim_rx_data;
64 sim_rx_addr = sim_rx_addr + 1;
65 end
66
67 // TX logic
68 if (tx_ready && `tx_valid) begin
69 tx_b_valid = 1;
70 tx_b_data = `tx_data;
71 end
72 if (tx_b_valid) begin
73 $write("%s", tx_b_data);
74 tx_b_valid = 0;
75 end
76 tx_ready = !tx_b_valid;
77 end
78end
79
80`endif
81
82endmodule
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..3f49cb6
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,56 @@
1#include <cstdint>
2#include <iostream>
3#include <verilated.h>
4#include <verilated_vcd_c.h>
5
6#include "Vtop.h"
7
8int main(int argc, const char *argv[])
9{
10 Verilated::commandArgs(argc, argv);
11
12 Verilated::traceEverOn(true);
13 VerilatedVcdC vcd;
14
15 Vtop top;
16 top.trace(&vcd, 100 /* levels of hierarchy */);
17
18 vcd.set_time_unit("ns");
19 vcd.set_time_resolution("ns");
20 vcd.open("build/out.vcd");
21
22 std::cout << "*** RESET SEQUENCE ***\n";
23
24 std::uint64_t time = 0;
25
26 top.clk = 0;
27 top.reset_n = 0;
28 top.eval();
29 vcd.dump(++time);
30
31 top.clk = 1;
32 top.eval();
33 vcd.dump(++time);
34
35 top.clk = 0;
36 top.reset_n = 1;
37 top.eval();
38 vcd.dump(++time);
39
40 std::cout << "*** MAIN LOOP ***\n";
41
42 for (unsigned int i = 0; i < 500 && !Verilated::gotFinish(); ++i) {
43 top.clk = 1;
44 top.eval();
45 vcd.dump(++time);
46 top.clk = 0;
47 top.eval();
48 vcd.dump(++time);
49 }
50
51 std::cout << "\n";
52
53 vcd.close();
54
55 return 0;
56}
diff --git a/ntoa.sv b/ntoa.sv
new file mode 100644
index 0000000..c55c9e6
--- /dev/null
+++ b/ntoa.sv
@@ -0,0 +1,83 @@
1module ntoa
2 #( BITS = 8
3 )
4 ( input bit clk
5 , input bit reset
6
7 , output bit n_ready
8 , input bit n_valid `define n_valid $past(n_valid)
9 , input bit [BITS-1:0] n_data `define n_data $past(n_data)
10
11 , input bit a_ready `define a_ready $past(a_ready)
12 , output bit a_valid
13 , output bit [7:0] a_data
14 );
15
16bit bcd_ready;
17bit bcd_valid;
18bit [b2b.DIGITS-1:0][b2b.BASE_BITS-1:0] bcd_data;
19
20bin2bcd
21 #( .BITS(BITS)
22 , .BASE(10)
23 ) b2b
24 ( .clk(clk)
25 , .reset(reset)
26
27 , .bin_ready(n_ready)
28 , .bin_valid(n_valid)
29 , .bin_data(n_data)
30
31 , .bcd_ready(bcd_ready)
32 , .bcd_valid(bcd_valid) `define bcd_valid $past(bcd_valid)
33 , .bcd_data(bcd_data) `define bcd_data $past(bcd_data)
34 );
35
36bit bcd_b_valid;
37bit [b2b.DIGITS-1:0][b2b.BASE_BITS-1:0] bcd_b_data;
38
39bit [$clog2(b2b.DIGITS):0] work;
40
41always_ff @(posedge clk) begin
42 if (reset) begin
43 a_valid = 0;
44 bcd_ready = 0;
45 bcd_b_valid = 0;
46 end else begin
47 if (bcd_ready && `bcd_valid) begin
48 bcd_b_valid = 1;
49 bcd_b_data = `bcd_data;
50 // verilator lint_off WIDTH
51 work = b2b.DIGITS;
52 // verilator lint_on WIDTH
53 for (int i = b2b.DIGITS; i > 1; i = i - 1) begin
54 if (bcd_b_data[b2b.DIGITS-1] != 0) break;
55 bcd_b_data = { bcd_b_data[b2b.DIGITS-2:0], {b2b.BASE_BITS{1'b0}} };
56 work = work - 1;
57 end
58 end
59
60 if (`a_ready) a_valid = 0;
61 if (!a_valid && bcd_b_valid) begin
62 if (work != 0) begin
63 a_valid = 1;
64 // verilator lint_off WIDTH
65 if (bcd_b_data[b2b.DIGITS-1] < 10)
66 a_data = "0" + bcd_b_data[b2b.DIGITS-1];
67 else
68 a_data = "a" + bcd_b_data[b2b.DIGITS-1] - 10;
69 // verilator lint_off WIDTH
70 bcd_b_data = { bcd_b_data[b2b.DIGITS-2:0], {b2b.BASE_BITS{1'b0}} };
71 work = work - 1;
72 end else begin
73 a_valid = 1;
74 a_data = ",";
75 bcd_b_valid = 0;
76 end
77 end
78
79 bcd_ready = !bcd_b_valid;
80 end
81end
82
83endmodule
diff --git a/rom.hex b/rom.hex
new file mode 100644
index 0000000..ee75917
--- /dev/null
+++ b/rom.hex
@@ -0,0 +1,18 @@
1@0
2
3// "Hello, world!\n"
448 65 6c 6c 6f 2c 20 77 6f 72 6c 64 21 0a
5
6// "I will now echo your input: "
749 20 77 69 6c 6c 20 6e 6f 77 20 65 63 68 6f 20
879 6f 75 72 20 69 6e 70 75 74 3a 20
9
10// At this point, will switch to ECHO mode until it reads a newline.
1100
12
13// "The Fibonacci sequence: "
1454 68 65 20 46 69 62 6f 6e 61 63 63 69 20 73 65
1571 75 65 6e 63 65 3a 20
16
17// At this point, will switch to FIB mode until end of time.
1800
diff --git a/top.sv b/top.sv
new file mode 100644
index 0000000..4acfdeb
--- /dev/null
+++ b/top.sv
@@ -0,0 +1,145 @@
1module top
2 #( FIB_BITS = 16
3 , ROM_BITS = 8
4 )
5 ( input bit clk // verilator public
6 , input bit reset_n // verilator public
7 );
8
9bit reset;
10assign reset = !reset_n;
11
12bit [7:0] rom [0:(1<<ROM_BITS)-1];
13initial $readmemh("rom.hex", rom);
14
15bit [ROM_BITS-1:0] addr;
16
17bit rx_ready;
18bit rx_valid;
19bit [7:0] rx_data;
20
21bit tx_ready;
22bit tx_valid;
23bit [7:0] tx_data;
24
25jtag_uart
26 #( .INSTANCE(0)
27 ) uart0
28 ( .clk(clk)
29 , .reset(reset)
30
31 , .rx_ready(rx_ready)
32 , .rx_valid(rx_valid) `define rx_valid $past(rx_valid)
33 , .rx_data(rx_data) `define rx_data $past(rx_data)
34
35 , .tx_ready(tx_ready) `define tx_ready $past(tx_ready)
36 , .tx_valid(tx_valid)
37 , .tx_data(tx_data)
38 );
39
40bit fib_ready;
41bit fib_valid;
42bit [FIB_BITS-1:0] fib_data;
43
44fibseq
45 #( .BITS(FIB_BITS)
46 ) fib
47 ( .clk(clk)
48 , .reset(reset)
49
50 , .ready(fib_ready)
51 , .valid(fib_valid)
52 , .data(fib_data)
53 );
54
55bit fib_a_ready;
56bit fib_a_valid;
57bit [7:0] fib_a_data;
58
59ntoa
60 #( .BITS(FIB_BITS)
61 ) fib_ntoa
62 ( .clk(clk)
63 , .reset(reset)
64
65 , .n_ready(fib_ready)
66 , .n_valid(fib_valid)
67 , .n_data(fib_data)
68
69 , .a_ready(fib_a_ready)
70 , .a_valid(fib_a_valid) `define fib_a_valid $past(fib_a_valid)
71 , .a_data(fib_a_data) `define fib_a_data $past(fib_a_data)
72 );
73
74enum
75 { INTRO_ECHO
76 , ECHO
77 , INTRO_FIB
78 , FIB
79 } state;
80
81bit tmp_valid;
82bit [7:0] tmp_data;
83
84always_ff @(posedge clk) begin
85 if (reset) begin
86 addr = 0;
87 rx_ready = 0;
88 tx_valid = 0;
89 fib_a_ready = 0;
90 state = state.first;
91 tmp_valid = 0;
92 end else unique0 case (state)
93
94 INTRO_ECHO, INTRO_FIB: begin
95 automatic bit [7:0] data = rom[addr];
96 if (`tx_ready) tx_valid = 0;
97 if (!tx_valid && (data != 0)) begin
98 tx_valid = 1;
99 tx_data = data;
100 addr = addr + 1;
101 end else if (data == 0) begin
102 addr = addr + 1;
103 state = state.next;
104 end
105 end
106
107 ECHO: begin
108 if (`tx_ready && tx_valid && tx_data == "\n") begin
109 // FIXME race; we aren't going to consume input this cycle, but we might have tmp_valid or rx_ready asserted
110 rx_ready = 0;
111 tx_valid = 0;
112 state = INTRO_FIB;
113 end else begin
114 if (`tx_ready) tx_valid = 0;
115 if (rx_ready && `rx_valid) begin
116 tmp_valid = 1;
117 tmp_data = `rx_data;
118 end
119 if (!tx_valid && tmp_valid) begin
120 tx_valid = 1;
121 tx_data = tmp_data;
122 tmp_valid = 0;
123 end
124 rx_ready = !tmp_valid;
125 end
126 end
127
128 FIB: begin
129 if (`tx_ready) tx_valid = 0;
130 if (fib_a_ready && `fib_a_valid) begin
131 tmp_valid = 1;
132 tmp_data = `fib_a_data;
133 end
134 if (!tx_valid && tmp_valid) begin
135 tx_valid = 1;
136 tx_data = tmp_data;
137 tmp_valid = 0;
138 end
139 fib_a_ready = !tmp_valid;
140 end
141
142 endcase
143end
144
145endmodule