eris2206

Documentation: http://frombelow.net/projects/eris2206/
Clone: git clone https://git.frombelow.net/eris2206.git
Log | Files | Refs | Submodules | README | LICENSE

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