]>
Commit | Line | Data |
---|---|---|
a051754e MG |
1 | `include "ram.v" |
2 | `include "prescaler.v" | |
3 | `include "single_trigger.v" | |
4 | `include "multiple_trigger.v" | |
5 | ||
6 | `define INST_JMP 3'b000 | |
7 | `define INST_JPR 3'b001 | |
8 | `define INST_LDN 3'b010 | |
9 | `define INST_STO 3'b011 | |
10 | `define INST_SUB 3'b100 | |
11 | `define INST_SKN 3'b110 | |
12 | `define INST_HALT 3'b111 | |
13 | ||
14 | `define PC_INC_BUTTON buttons[8] | |
15 | `define PC_DEC_BUTTON buttons[9] | |
16 | `define PAGE_INC_BUTTON buttons[10] | |
17 | `define PAGE_DEC_BUTTON buttons[11] | |
18 | `define PC_CLR_BUTTON buttons[12] | |
19 | `define ACCUM_CLR_BUTTON buttons[13] | |
20 | `define RUN_BUTTON buttons[14] | |
21 | `define EXECUTE_BUTTON buttons[15] | |
22 | ||
23 | function [7:0] reverse (input [7:0] forward); | |
24 | integer i; | |
25 | for (i = 0; i < 8; i = i + 1) | |
26 | reverse[7-i] = forward[i]; | |
27 | endfunction | |
28 | ||
29 | function [7:0] select (input condition, input [7:0] word); | |
30 | integer i; | |
31 | select = condition ? word : 8'b00000000; | |
32 | endfunction | |
33 | ||
34 | // This is a thirty-two bit accumulator machine identical to the Manchester SSEM. | |
35 | ||
36 | module PROCESSOR (input clk, output [23:0] led, output [3:0] indicators, input [15:0] buttons); | |
37 | ||
38 | // We use two clocks - the main one for all the registers and one for the RAM | |
39 | // The RAM clock is twice as fast as the register one to simulate the effect | |
40 | // of flow through RAM which is not supported on the FPGA | |
41 | ||
42 | wire clock; | |
43 | ||
44 | PRESCALER #(.BITS(2)) scal0 (.clk(clk), .out(clock)); | |
45 | ||
46 | // Handle running | |
47 | ||
48 | reg running = 0; | |
49 | ||
50 | always @ (posedge `RUN_BUTTON) begin | |
51 | ||
52 | running <= !running; | |
53 | ||
54 | end | |
55 | ||
56 | // Generate running clock | |
57 | ||
58 | wire running_counter; | |
59 | ||
60 | PRESCALER #(.BITS(6)) scal1 (.clk(clock), .out(running_counter)); | |
61 | ||
62 | wire running_clk = running & running_counter; | |
63 | ||
64 | // Handle execution | |
65 | ||
66 | wire [4:0] execute_trigger; | |
67 | ||
68 | MULTIPLE_TRIGGER #(.BITS(4)) trig0 (.clk(clock), .trigger_in(!running & `EXECUTE_BUTTON), .trigger_out(execute_trigger)); | |
69 | ||
70 | wire [4:0] running_trigger; | |
71 | ||
72 | MULTIPLE_TRIGGER #(.BITS(5)) trig1 (.clk(clock), .trigger_in(running_clk), .trigger_out(running_trigger)); | |
73 | ||
74 | wire [4:0] execute = execute_trigger | running_trigger; | |
75 | ||
76 | // Handle halt | |
77 | ||
78 | reg halt = 0; | |
79 | ||
80 | wire newHalt = execute[3] & (inst == `INST_HALT) ? 1 : | |
81 | !running ? 0 : | |
82 | halt; | |
83 | ||
84 | always @ (posedge clock) begin | |
85 | ||
86 | halt <= newHalt; | |
87 | ||
88 | end | |
89 | ||
90 | // Handle program space | |
91 | ||
92 | // Note that this uses the RAM clock even for the buffer update. Failing to do results in two buffer updates | |
93 | // which leaves the RAM contents unchanged. | |
94 | ||
95 | wire [31:0] pOut; | |
96 | ||
97 | wire [7:0] reverseButtons = reverse(buttons[7:0]); | |
98 | ||
99 | wire [31:0] buffer = { select(page == 2'b11, reverseButtons), select(page == 2'b10, reverseButtons), select(page == 2'b01, reverseButtons), select(page == 2'b00, reverseButtons) } ^ pOut; | |
100 | ||
101 | wire write_trigger; | |
102 | ||
103 | wire program_buttons = (buttons[0] | buttons[1] | buttons[2] | buttons[3] | buttons[4] | buttons[5] | buttons[6] | buttons[7]); | |
104 | ||
105 | SINGLE_TRIGGER trig2 (.clk(clk), .trigger_in(program_buttons), .trigger_out(write_trigger)); | |
106 | ||
107 | wire [4:0] address = (execute[2] | execute[3]) ? addr : pc; | |
108 | ||
109 | wire [31:0] pIn = execute[2] & (inst == `INST_STO) ? accum : buffer; | |
110 | ||
111 | wire write_enable = (!running & write_trigger) | (execute[2] & (inst == `INST_STO)); | |
112 | ||
113 | RAM #(.DATA_BITS(32),.ADDRESS_BITS(5)) programMemory (.clk(clk), .write(write_enable), .addr(address), .in_data(pIn), .out_data(pOut)); | |
114 | ||
115 | // Handle page | |
116 | ||
117 | reg [1:0] page = 0; | |
118 | ||
119 | wire page_prev_trigger; | |
120 | wire page_next_trigger; | |
121 | ||
122 | SINGLE_TRIGGER trig7 (.clk(clock), .trigger_in(`PAGE_DEC_BUTTON), .trigger_out(page_prev_trigger)); | |
123 | SINGLE_TRIGGER trig8 (.clk(clock), .trigger_in(`PAGE_INC_BUTTON), .trigger_out(page_next_trigger)); | |
124 | ||
125 | wire [1:0] newPage = page_prev_trigger ? (page + 2'b11) : page_next_trigger ? (page + 2'b01) : page; | |
126 | ||
127 | always @ (posedge clock) begin | |
128 | ||
129 | page <= newPage; | |
130 | ||
131 | end | |
132 | ||
133 | // Handle PC | |
134 | ||
135 | reg [4:0] pc = 0; | |
136 | ||
137 | wire [4:0] nextPc = pc + 5'b00001; | |
138 | wire [4:0] prevPc = pc + 5'b11111; | |
139 | ||
140 | wire pc_prev_trigger; | |
141 | wire pc_next_trigger; | |
142 | wire pc_zero_trigger; | |
143 | ||
144 | SINGLE_TRIGGER trig3 (.clk(clock), .trigger_in(`PC_INC_BUTTON), .trigger_out(pc_next_trigger)); | |
145 | SINGLE_TRIGGER trig4 (.clk(clock), .trigger_in(`PC_DEC_BUTTON), .trigger_out(pc_prev_trigger)); | |
146 | SINGLE_TRIGGER trig5 (.clk(clock), .trigger_in(`PC_CLR_BUTTON), .trigger_out(pc_zero_trigger)); | |
147 | ||
148 | wire [4:0] newPc = execute[3] & (inst == `INST_JMP) ? pOut[4:0] : | |
149 | execute[3] & (inst == `INST_JPR) ? pc + pOut[4:0] : | |
150 | execute[3] & (inst == `INST_SKN) & (accum[31] == 1) ? nextPc : | |
151 | !halt & execute[0] ? nextPc : | |
152 | !running & pc_zero_trigger ? 5'b00000 : | |
153 | !running & pc_next_trigger ? nextPc : | |
154 | !running & pc_prev_trigger ? prevPc : | |
155 | pc; | |
156 | ||
157 | always @ (posedge clock) begin | |
158 | ||
159 | pc <= newPc; | |
160 | ||
161 | end | |
162 | ||
163 | // Handle instruction | |
164 | ||
165 | reg [2:0] inst = 0; | |
166 | reg [4:0] addr = 0; | |
167 | ||
168 | wire [2:0] newInst = execute[1] ? pOut[15:13] : | |
169 | inst; | |
170 | ||
171 | wire [4:0] newAddr = execute[1] ? pOut[4:0] : | |
172 | addr; | |
173 | ||
174 | always @ (posedge clock) begin | |
175 | ||
176 | inst <= newInst; | |
177 | ||
178 | addr <= newAddr; | |
179 | ||
180 | end | |
181 | ||
182 | // Handle accumulator | |
183 | ||
184 | reg [31:0] accum = 0; | |
185 | ||
186 | wire accum_zero_trigger; | |
187 | ||
188 | SINGLE_TRIGGER trig6 (.clk(clock), .trigger_in(`ACCUM_CLR_BUTTON), .trigger_out(accum_zero_trigger)); | |
189 | ||
190 | wire [31:0] newAccum = execute[2] & (inst == `INST_LDN) ? 0 - pOut : | |
191 | execute[2] & (inst == `INST_SUB) ? accum - pOut : | |
192 | !running & accum_zero_trigger ? 0 : | |
193 | accum; | |
194 | ||
195 | always @ (posedge clock) begin | |
196 | ||
197 | accum <= newAccum; | |
198 | ||
199 | end | |
200 | ||
201 | // Assign the outputs | |
202 | ||
203 | wire [7:0] selectedOut = (page == 2'b00) ? pOut[7:0] : | |
204 | (page == 2'b01) ? pOut[15:8] : | |
205 | (page == 2'b10) ? pOut[23:16] : | |
206 | pOut[31:24]; | |
207 | ||
208 | wire [7:0] selectedAccum = (page == 2'b00) ? accum[7:0] : | |
209 | (page == 2'b01) ? accum[15:8] : | |
210 | (page == 2'b10) ? accum[23:16] : | |
211 | accum[31:24]; | |
212 | ||
213 | assign led[7:0] = reverse(selectedOut); | |
214 | assign led[15:8] = reverse(selectedAccum); | |
215 | assign led[23:16] = {page, 1'b0, pc}; | |
216 | assign indicators = {1'b0, (!running & `EXECUTE_BUTTON) | running_clk, halt, running & !halt}; | |
217 | ||
218 | endmodule | |
219 |