ecpu.v (21066B)
1 /* 2 * Copyright 2022 Gerd Beuster (gerd@frombelow.net). This is free 3 * soft-/hardware under the GNU GPL v3 license or any later 4 * version. See COPYING in the root directory for details. 5 */ 6 7 `ifndef ecpu_v 8 `define ecpu_v 9 10 `include "uart.v" 11 `include "ws2811.v" 12 13 module esoc( // System operates at this clock speed in run mode 14 input slow_clk, 15 // Resets counters and registers, RAM is not deleted! 16 input reset_in, 17 // Switch between memory set mode andexecution mode 18 input program_mode, 19 // Single step mode or continous execution 20 input single_cont, 21 // Step trigger in single step mode, write data in 22 // set memory mode. 23 input step, 24 // Skip memory cell in set memory mode 25 input skip, 26 input [7:0] din, // Data bus input in set memory mode 27 // GPIO (LEDs) 28 output green, output red_n, output red_e, output red_s, 29 output red_w, 30 // Fast clock for timing sensitive modules (UART & WS2811) 31 input fast_clk, 32 // UART 33 output tx, input rx, 34 // WS2811 35 output ws2811_dout); 36 37 // The default ROM contains two programs: 38 // 39 // - Computation of Fibonacci numbers (at $80) 40 // 41 // This program is located at the start of the ROM, and thus gets 42 // executed on power on 43 // 44 // - Second stage bootloader (at $C0) 45 // 46 // The bootloader loads a program via the serial line. (See 47 // tools/send_serial.py for the upload program.) In order to use it, 48 // the user has to ener a minimal first stage bootloader, which 49 // jumps into this second stage bootloader, via the front panel 50 // switches. 51 parameter rom_file = "roms/rom.dat"; 52 53 // This ROM shows a sequence on the built-in LEDs of the 54 // iCEstick. Since the iCEstick is embedded in the case, the LEDs 55 // are only visible when the cas is opened. 56 // parameter rom_file = "roms/rom_leds.dat"; 57 58 // A serial echo program to test the UART. 59 // parameter rom_file = "roms/rom_uart.dat"; 60 61 // The program to test all opcodes had to be split in two parts. As 62 // the result of each opcode test, the value $FF should be written 63 // to memory. 64 // parameter rom_file = "roms/rom_opcode_test_0.dat"; 65 // parameter rom_file = "roms/rom_opcode_test_1.dat"; 66 67 // 68 // Operation modes 69 // 70 71 // There are two main modes: 72 // 73 // - In execution mode, the CPU runs all components 74 // 75 // - In memory set mode, modul manual_programmer controls the 76 // address bus and the memory write line. Data input comes straight 77 // from the switches set by the user. 78 wire [7:0] data_bus, address_bus, cpu_address_bus, programmer_address_bus; 79 wire mem_set, cpu_mem_set, programmer_mem_set; 80 wire cpu_clk; 81 // In programming mode, the CPU clock is stopped. In exeuction mode, it 82 // normally runs on the slow clock, unless we are in singel step mode. 83 assign cpu_clk = program_mode ? 0 : 84 ((single_cont | reset_in) ? slow_clk : step); 85 assign mem_set = program_mode ? programmer_mem_set : cpu_mem_set; 86 assign data_bus = program_mode ? din : cpu_dout; 87 assign address_bus = program_mode ? programmer_address_bus : cpu_address_bus; 88 89 manual_programmer mp(.clk(fast_clk), .res(reset_in), 90 .step(step), .skip(skip), 91 .mem_set(programmer_mem_set), 92 .aout(programmer_address_bus)); 93 94 // 95 // Glue logic: Memory mapping of peripherals 96 // 97 98 // The CPU addresses a flat memory space. Here we define glue logic 99 // to map this flat memory space to access to RAM, ROM, UART, and 100 // GPIO. 101 // 102 // The lower 5 bits of $FD turn the LEDs of the iCEstick on and off. 103 104 // hFF : UART - Write: Clear recv_buffer_full 105 // - Read: Status (0: TX busy, 1: recv_buffer_full) 106 // hFE : UART - Write: Send Data - Read: Received Data 107 // $FD : I/O (0..5: LEDs) 108 // h80-hFC : ROM 109 // h00-h7F : RAM 110 111 // Data input for the CPU may come from ROM, RAM, or UART. 112 wire [7:0] mem_dout; // CPU data (memory) input 113 // Data output of ROM, RAM, and UART 114 wire [7:0] rom_dout, ram_dout, uart_dout; 115 // Map data output to CPU data input according to memory map 116 assign mem_dout = (address_bus < 8'h80) ? ram_dout : 117 ((address_bus < 8'hFE) ? rom_dout : uart_dout); 118 wire [7:0] cpu_dout; 119 // Set correct device to write mode 120 wire ram_set, gpio_set, uart_set; 121 assign ram_set = (address_bus < 8'h80) ? mem_set : 0; 122 assign gpio_set = (address_bus == 8'hFD) ? mem_set : 0; 123 assign uart_set = (address_bus > 8'hFD) ? mem_set : 0; 124 125 // 126 // CPU and peripherals definitions 127 // 128 129 wire [15:0] control_logic; 130 wire [7:0] reg_a_value; 131 wire [7:0] alu_value; 132 wire [7:0] pc_value; 133 wire [7:0] dp_value; 134 ecpu e(.clk(cpu_clk), 135 .reset_in(reset_in), 136 .mem_dout(mem_dout), 137 .mem_set(cpu_mem_set), 138 .data_bus(cpu_dout), 139 .address_bus(cpu_address_bus), 140 .ctrl_logic_mon(control_logic), 141 .reg_a_value(reg_a_value), 142 .alu_value(alu_value), 143 .pc_value(pc_value), 144 .dp_value(dp_value)); 145 146 ram ram_mem(.clk(fast_clk), .set(ram_set), 147 .din(data_bus), .dout(ram_dout), .ain(address_bus[6:0])); 148 149 rom 150 #(.rom_file(rom_file)) 151 rom_mem(.clk(fast_clk), .dout(rom_dout), .ain(address_bus[6:0])); 152 153 154 gpio g(.clk(fast_clk), .din(data_bus[4:0]), .write(gpio_set), 155 .green(green), .red_n(red_n), .red_e(red_e), 156 .red_s(red_s), .red_w(red_w)); 157 158 // UART reg_select is connected to the LSB of the address bus, thus 159 // mapping the two UART registers to two consequtive memory addresses. 160 // The start wire connects to the write signal of the CPU: Writing 161 // data two the UART starts transmission. 162 // The UART must be driven by a 12 Mhz clock. 163 uart u(.clk(fast_clk), .din(data_bus), .dout(uart_dout), 164 .reg_select(address_bus[0]), .write(uart_set), .tx(tx), .rx(rx)); 165 166 // Monitor: Show internal state of system on LED matrix 167 `define VRAM_SIZE 32 168 led_monitor #(.vram_size(`VRAM_SIZE)) 169 led_mon (.fast_clk(fast_clk), 170 .data_bus(data_bus), 171 .address_bus(address_bus), 172 .mem_set(mem_set), 173 .control_logic(control_logic), 174 .reg_a(reg_a_value), 175 .alu(alu_value), 176 // We set value to address bus in program mode in order 177 // to highlight the address currently manipulated 178 .pc(program_mode ? address_bus : pc_value), 179 .dp(dp_value), 180 .ws2811_dout(ws2811_dout)); 181 182 endmodule // esoc 183 184 module ecpu(input clk, 185 input reset_in, 186 input [7:0] mem_dout, 187 output mem_set, 188 output [7:0] data_bus, 189 output [7:0] address_bus, 190 output [15:0] ctrl_logic_mon, // Monitor output 191 output [7:0] reg_a_value, 192 output [7:0] alu_value, 193 output [7:0] pc_value, 194 output [7:0] dp_value); 195 196 // While the control logic of the CPU operates on negative clock 197 // edges, the components operate on positive edges. 198 199 // Enable/disable lines for components 200 // PC 201 wire pc_set, pc_inc; 202 // The comperator connects microcode for conditional 203 // (pc_set_if_zero, pc_set_if_nonzero) and unconditional 204 // (pc_set_uncond) jumps to the set signal of the PC (pc_set). 205 wire pc_set_if_zero, pc_set_if_nonzero, pc_set_uncond; 206 // MIP 207 wire mip_set; 208 // DP 209 wire dp_set, dp_get; 210 // Accumulator 211 wire reg_a_get, reg_a_zero; 212 // Accumulator does not only allow setting, but more 213 // commands (inc, dec, ...). 214 wire [2:0] reg_a_cmd; 215 // ALU 216 // Three bits determine which result we get from the ALU: 217 `define alu_off 3'b000 218 `define alu_add 3'b001 219 `define alu_sub 3'b010 220 // Multiplication and division occpuy too many PLBs 221 /* -----\/----- EXCLUDED -----\/----- 222 `define alu_mul 3'b011 223 `define alu_div 3'b100 224 -----/\----- EXCLUDED -----/\----- */ 225 `define alu_and 3'b101 226 `define alu_or 3'b110 227 `define alu_xor 3'b111 228 wire [2:0] alu_get; 229 230 // Glue logic: Since all components may write to the buses, write 231 // access to the buses must be multiplexed. 232 wire [7:0] pc_dout, dp_dout, mip_dout, reg_a_dout, alu_dout; 233 // Multiplexer for address bus: 234 // If dp_get is set, the data pointer is loaded on the address bus. 235 // Otherwise the program counter is loaded on the address bus. 236 assign address_bus = dp_get ? dp_dout : pc_dout; 237 // Multiplexer for data bus 238 // Unless the data bus is explicitly loaded with the accumulator 239 // or the ALU, it is loaded from memory. 240 wire [7:0] alu_sum; 241 wire [7:0] alu_difference; 242 wire [7:0] alu_and; 243 wire [7:0] alu_or; 244 wire [7:0] alu_xor; 245 assign alu_value = (alu_get == `alu_add) ? alu_sum : 246 ((alu_get == `alu_sub) ? alu_difference : 247 ((alu_get == `alu_and) ? alu_and : 248 ((alu_get == `alu_or) ? alu_or : 249 ((alu_get == `alu_xor) ? alu_xor : 250 alu_sum)))); 251 assign data_bus = reg_a_get ? reg_a_dout : 252 ((alu_get == `alu_off) ? mem_dout : alu_value); 253 // Monitor output 254 assign ctrl_logic_mon = { clk, pc_set_uncond, pc_set_if_zero, 255 pc_set_if_nonzero, 256 pc_inc, dp_set, dp_get, 257 mip_set, reg_a_cmd, reg_a_get, 258 mem_set, alu_get } ; 259 assign reg_a_value = reg_a_dout; 260 assign pc_value = pc_dout; 261 assign dp_value = dp_dout; 262 263 // Reset logic 264 reg res = 1; 265 always @(posedge(clk)) 266 res <= reset_in; 267 268 269 control_logic ctrl (.clk(clk), 270 .res(res), 271 .pc_set_uncond(pc_set_uncond), 272 .pc_set_if_zero(pc_set_if_zero), 273 .pc_set_if_nonzero(pc_set_if_nonzero), 274 .pc_inc(pc_inc), 275 .dp_set(dp_set), .dp_get(dp_get), 276 .mip_set(mip_set), 277 .mip_value(mip_dout), 278 .reg_a_cmd(reg_a_cmd), .reg_a_get(reg_a_get), 279 .mem_set(mem_set), 280 .alu_get(alu_get)); 281 282 283 // Program Counter 284 counter 285 #(.start_value(8'h80)) // Start execution in ROM 286 pc (.clk(clk), .res(res), 287 .set(pc_set), .inc(pc_inc), 288 .din(data_bus), .dout(pc_dout)); 289 290 // The micro instruction pointer (mip) keeps track of the current 291 // micro instruction. It is updated from the data bus (when the 292 // next opcode is read, because opcode = pointer to begin of 293 // microcode implementation of opcode) and read by the control 294 // logic. 295 // 296 // The increment input is fixed to high, because the MIP shall 297 // always step to the next instruction always, unless a new 298 // value is set. 299 counter mip (.clk(clk), .res(res), 300 .set(mip_set), .inc(1'b1), 301 .din(data_bus), .dout(mip_dout)); 302 303 // The data pointer reads from data bus and writes to the address 304 // bus. It is used to dereference memory addresses. 305 register dp (.clk(clk), .res(res), 306 .set(dp_set), .inc(1'b0), .dec(1'b0), 307 .rol(1'b0), .ror(1'b0), .inv(1'b0), 308 .din(data_bus), .dout(dp_dout)); 309 310 // Accumulator 311 // The accumulator supports additional operations besides setting. 312 // We demultiplex the reg_a_cmd for this 313 wire reg_a_set, reg_a_inc, reg_a_dec, reg_a_rol, 314 reg_a_ror, reg_a_inv; 315 assign reg_a_set = (reg_a_cmd == 3'b001); 316 assign reg_a_inc = (reg_a_cmd == 3'b010); 317 assign reg_a_dec = (reg_a_cmd == 3'b011); 318 assign reg_a_rol = (reg_a_cmd == 3'b100); 319 assign reg_a_ror = (reg_a_cmd == 3'b101); 320 assign reg_a_inv = (reg_a_cmd == 3'b110); 321 register reg_a (.clk(clk), .res(res), 322 .set(reg_a_set), .inc(reg_a_inc), .dec(reg_a_dec), 323 .rol(reg_a_rol), .ror(reg_a_ror), .inv(reg_a_inv), 324 .din(data_bus), .dout(reg_a_dout), .zero(reg_a_zero)); 325 326 // Comperator 327 // The program counter is set from the data bus (i.e. a jump is 328 // executed) when we execute the microcode for an unconditional 329 // jump (pc_set_uncond) or when we execute the microcode for a 330 // conditional jump (pc_set_if_zero) and the zero flag of the 331 // accumulator is set. 332 assign pc_set = pc_set_uncond | (pc_set_if_zero & reg_a_zero) | 333 (pc_set_if_nonzero & !reg_a_zero); 334 335 // ALU 336 // The ALU consists of an adder and a register. The adders gets the 337 // accumulator and the data bus as input and updates the register. 338 // The register writes to the data bus. 339 alu a(.clk(clk), .din_a(reg_a_dout), .din_b(data_bus), .sum(alu_sum), 340 .difference(alu_difference), .band(alu_and), .bor(alu_or), 341 .bxor(alu_xor)); 342 endmodule 343 344 module counter(input clk, 345 input res, 346 input set, 347 input inc, 348 input [7:0] din, 349 output [7:0] dout); 350 351 // Start value is only set on initialization. Reset 352 // always sets counter to 0, not back to start value. 353 // By setting the start value of the PC to the first 354 // ROM address, the SOC will run from ROM on power-up, 355 // but can be programmed starting from address h00 356 // on reset. 357 parameter start_value = 8'h00; 358 reg [7:0] value; 359 reg init = 0; 360 361 assign dout = value; 362 always @(posedge(clk)) 363 if (init == 0) 364 begin 365 value <= start_value; 366 init <= 1; 367 end 368 else 369 if (res) 370 value <= 8'h00; 371 else if (set) 372 value <= din; 373 else if (inc) 374 // Only increment value if no new value is set. This order is 375 // important for two reasons: 376 // - Conditional jumps always increment the PC. 377 // This should only have an effect if no new address is set. 378 // - For the MIP, inc is fixed to high. If mip_set sets a new 379 // new address, the new address should not increment immediately. 380 value <= value + 1; 381 endmodule 382 383 module alu(input clk, input [7:0] din_a, input [7:0] din_b, 384 output reg [7:0] sum, 385 output reg [7:0] difference, 386 output reg [7:0] band, 387 output reg [7:0] bor, 388 output reg [7:0] bxor); 389 always @(posedge(clk)) 390 begin 391 sum <= din_a + din_b; 392 difference <= din_a - din_b; 393 // Multiplication and division occpuy too many PLBs 394 /* -----\/----- EXCLUDED -----\/----- 395 product <= din_a * din_b; 396 quotient <= din_a / din_b; 397 -----/\----- EXCLUDED -----/\----- */ 398 band <= din_a & din_b; 399 bor <= din_a | din_b; 400 bxor <= din_a ^ din_b; 401 end 402 endmodule 403 404 module register(input clk, 405 input res, 406 input set, 407 input inc, 408 input dec, 409 input rol, 410 input ror, 411 input inv, 412 input [7:0] din, 413 output [7:0] dout, 414 output zero); 415 416 reg [7:0] value; 417 418 assign dout = value; 419 assign zero = (value == 0); 420 always @(posedge(clk)) 421 if (res) 422 value <= 8'b00000000; 423 else if (set) 424 value <= din; 425 else if (inc) 426 value <= value + 1; 427 else if (dec) 428 value <= value - 1; 429 else if (rol) 430 value <= { value[6], value[5], value[4], value[3], 431 value[2], value[1], value[0], value[7] }; 432 else if (ror) 433 value <= { value[0], value[7], value[6], value[5], 434 value[4], value[3], value[2], value[1] }; 435 else if (inv) 436 value <= value ^ 8'b11111111; 437 endmodule 438 439 // 128 byte RAM module 440 module ram(input clk, 441 input set, // Write din to memory cell 442 input [7:0] din, 443 output reg [7:0] dout, 444 input [6:0] ain); 445 446 reg [7:0] mem [0:127]; 447 always @(posedge(clk)) 448 begin 449 if (set) 450 mem[ain] <= din; 451 dout <= mem[ain]; 452 end 453 endmodule 454 455 // 128 byte ROM module 456 module rom(input clk, 457 output reg [7:0] dout, 458 input [6:0] ain); 459 460 parameter rom_file = "roms/rom.dat"; 461 462 reg [7:0] mem [0:127]; 463 464 always @(posedge(clk)) 465 begin 466 dout <= mem[ain]; 467 end 468 469 initial 470 $readmemh(rom_file, mem); 471 endmodule 472 473 module gpio(input clk, input [4:0] din, 474 input write, 475 output reg green, output reg red_n, output reg red_e, 476 output reg red_s, output reg red_w); 477 478 always @(posedge(clk)) 479 if (write) 480 { red_n, red_e, red_s, red_w, green} <= din; 481 endmodule 482 483 // All microcode is stored in array microcode_(lsb|msb). The data for 484 // this arrays is generated by tools/mc_compiler.py. Pointers to the 485 // microcode for an opcode are stored in array ptr_opcode_microcode. 486 // These arrays are initialized upon reset. 487 module control_logic(input clk, input res, 488 output reg pc_set_uncond, 489 output reg pc_set_if_zero, 490 output reg pc_set_if_nonzero, 491 output reg pc_inc, 492 output reg dp_set, dp_get, 493 output reg mip_set, 494 input [7:0] mip_value, 495 output reg mem_set, 496 output reg reg_a_get, 497 output reg [2:0] reg_a_cmd, 498 output reg [2:0] alu_get); 499 500 // Used to set all enable/disable signals in one go 501 `define execute_microcode {alu_get, \ 502 mip_set, \ 503 reg_a_cmd, reg_a_get, \ 504 mem_set, \ 505 dp_get, dp_set, \ 506 pc_inc, \ 507 pc_set_if_nonzero, pc_set_if_zero, pc_set_uncond} 508 509 reg [7:0] microcode_lsb [0:145]; 510 reg [6:0] microcode_msb [0:145]; 511 512 // Microcode for the assembler opcodes is defined in 513 // tools/mc_compiler.py. During synthesis, 514 // mc_compiler.py writes the microcode for the assembler 515 // opcodes into a ROM file, which is read here. 516 initial 517 begin 518 $readmemh("tools/microcode_rom_lsb.dat", microcode_lsb); 519 $readmemh("tools/microcode_rom_msb.dat", microcode_msb); 520 end 521 522 always @(negedge(clk)) 523 if (res) 524 `execute_microcode <= 0; 525 else 526 `execute_microcode <= { microcode_msb[mip_value], 527 microcode_lsb[mip_value] }; 528 529 endmodule 530 531 module manual_programmer(input clk, input res, 532 input step, 533 input skip, 534 output reg mem_set, 535 output reg [7:0] aout); 536 reg state; 537 // The monitor (LED matrix) does not access the system memory. 538 // Instead it listens to the address and data busses and maintainces 539 // its own memory. Since the monitor update operation either updates 540 // the registers or the memory, it may miss a memory updates if we 541 // update the memory too fast here. In order to fix this problem, the 542 // write signal is kept up for time given by write_delay. 543 reg [3:0] write_delay; 544 `define state_set_data 0 545 `define state_write_or_skip_data 1 546 initial 547 begin 548 state <= `state_set_data; 549 aout <= 8'h00; 550 mem_set <= 0; 551 end 552 always @(posedge(clk)) 553 if (res) 554 begin 555 state <= `state_set_data; 556 aout <= 8'h00; 557 mem_set <= 0; 558 end 559 else 560 case (state) 561 `state_set_data: 562 if (step || skip) 563 begin 564 state <= `state_write_or_skip_data; 565 mem_set <= !skip; 566 write_delay <= -1; 567 end 568 `state_write_or_skip_data: 569 begin 570 if (write_delay == 0) 571 begin 572 mem_set <= 0; 573 state <= `state_set_data; 574 aout <= aout + 1; 575 end 576 write_delay <= write_delay - 1; 577 end 578 endcase 579 endmodule 580 581 `endif