hp-saturn/saturn_core.v
Raphael Jacquot 62a1624846 add license
add some testing stuff, not compelling :-(
2019-02-20 09:17:37 +01:00

647 lines
16 KiB
Verilog
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
(c) Raphaël Jacquot 2019
This file is part of hp_saturn.
hp_saturn is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
any later version.
hp_saturn is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Foobar. If not, see <https://www.gnu.org/licenses/>.
*/
`default_nettype none //
`include "def-clocks.v"
// `include "bus_commands.v"
// `include "hp48_00_bus.v"
// `include "dbg_module.v"
`include "saturn_decoder.v"
`include "saturn_alu.v"
`include "saturn_bus_ctrl.v"
/**************************************************************************************************
*
*
*
*
*
*/
`ifdef SIM
module saturn_core (
i_clk,
i_reset,
o_halt,
o_bus_reset,
i_bus_data_in,
o_bus_data_out,
o_bus_strobe,
o_bus_cmd_data,
o_phase
);
input wire [0:0] i_clk;
input wire [0:0] i_reset;
output wire [0:0] o_halt;
`else
module saturn_core (
clk_25mhz,
btn,
led,
o_bus_reset,
i_bus_data_in,
o_bus_data_out,
o_bus_strobe,
o_bus_cmd_data,
o_phase
);
input wire [0:0] clk_25mhz;
input wire [6:0] btn;
output reg [7:0] led;
wire [0:0] i_clk;
wire [0:0] i_reset;
assign i_clk = clk_25mhz;
assign i_reset = btn[1];
`endif
output wire [1:0] o_phase;
assign o_phase = clk_phase + 3;
output wire [0:0] o_bus_reset;
input wire [3:0] i_bus_data_in;
output wire [3:0] o_bus_data_out;
output wire [0:0] o_bus_strobe;
output wire [0:0] o_bus_cmd_data;
// clocks
reg [1:0] clk_phase;
reg [0:0] en_reset;
reg [0:0] ck_debugger; // phase 0
reg [0:0] ck_bus_send; // phase 0
reg [0:0] ck_bus_recv; // phase 1
reg [0:0] ck_bus_ecmd; // phase 3
reg [0:0] ck_inst_dec; // phase 2
reg [0:0] ck_inst_exe; // phase 3
reg [0:0] ck_alu_dump; // phase 0
reg [0:0] ck_alu_init; // phase 3
reg [0:0] ck_alu_prep; // phase 1
reg [0:0] ck_alu_calc; // phase 2
reg [0:0] ck_alu_save; // phase 3
reg [0:0] clock_end;
reg [31:0] cycle_ctr;
reg [31:0] max_cycle;
// hp48_bus bus_ctrl (
// .strobe (bus_strobe),
// .reset (reset),
// .address (bus_address),
// .command (bus_command),
// .nibble_in (bus_nibble_in),
// .nibble_out (bus_nibble_out),
// .bus_error (bus_error)
// );
saturn_decoder m_decoder (
.i_clk (i_clk),
.i_reset (i_reset),
.i_cycles (cycle_ctr),
.i_en_dbg (ck_debugger),
.i_en_dec (ck_inst_dec),
.i_pc (reg_pc),
.i_bus_load_pc (alu_bus_load_pc),
.i_stalled (dec_stalled),
.i_nibble (ctrl_bus_nibble_in),
.i_reg_p (reg_p),
.o_inc_pc (inc_pc),
.o_push (push),
.o_pop (pop),
.o_dec_error (inv_opcode),
.o_alu_debug (alu_debug),
.o_ins_addr (ins_addr),
.o_ins_decoded (ins_decoded),
.o_fields_table (fields_table),
.o_field (field),
.o_field_start (field_start),
.o_field_last (field_last),
.o_imm_value (imm_value),
.o_alu_op (alu_op),
.o_alu_no_stall (alu_no_stall),
.o_reg_dest (reg_dest),
.o_reg_src1 (reg_src1),
.o_reg_src2 (reg_src2),
.o_ins_rtn (ins_rtn),
.o_set_xm (set_xm),
.o_set_carry (set_carry),
.o_test_carry (test_carry),
.o_carry_val (carry_val),
.o_ins_set_mode (ins_set_mode),
.o_mode_dec (mode_dec),
.o_ins_alu_op (ins_alu_op),
.o_ins_test_go (ins_test_go),
.o_ins_reset (ins_reset),
.o_ins_config (ins_config),
.o_ins_mem_xfr (ins_mem_xfr),
.o_xfr_dir_out (xfr_dir_out)
);
wire [0:0] inc_pc;
wire [0:0] push;
wire [0:0] pop;
wire [0:0] inv_opcode;
wire [0:0] alu_debug;
wire [19:0] ins_addr;
wire [0:0] ins_decoded;
wire [1:0] fields_table;
wire [3:0] field;
wire [3:0] field_start;
wire [3:0] field_last;
wire [3:0] imm_value;
wire [4:0] alu_op;
wire [0:0] alu_no_stall;
wire [4:0] reg_dest;
wire [4:0] reg_src1;
wire [4:0] reg_src2;
wire [0:0] ins_rtn;
wire [0:0] set_xm;
wire [0:0] set_carry;
wire [0:0] test_carry;
wire [0:0] carry_val;
wire [0:0] ins_set_mode;
wire [0:0] mode_dec;
wire [0:0] ins_alu_op;
wire [0:0] ins_test_go;
wire [0:0] ins_reset;
wire [0:0] ins_config;
wire [0:0] ins_mem_xfr;
wire [0:0] xfr_dir_out;
wire [0:0] ins_unconfig;
saturn_alu m_alu (
.i_clk (i_clk),
.i_reset (i_reset),
.i_clk_ph (clk_phase[1:0]),
.i_cycle_ctr (cycle_ctr),
.i_en_alu_dump (ck_alu_dump),
.i_en_alu_prep (ck_alu_prep),
.i_en_alu_calc (ck_alu_calc),
.i_en_alu_init (ck_alu_init),
.i_en_alu_save (ck_alu_save),
.i_stalled (alu_stalled),
.o_bus_address (alu_bus_address),
.o_bus_xfr_cnt (alu_bus_xfr_cnt),
.o_bus_load_pc (alu_bus_load_pc),
.o_bus_load_dp (alu_bus_load_dp),
.o_bus_pc_read (alu_bus_pc_read),
.o_bus_dp_read (alu_bus_dp_read),
.o_bus_dp_write (alu_bus_dp_write),
.o_bus_config (alu_bus_config),
.i_bus_nibble_in (ctrl_bus_nibble_in),
.o_bus_nibble_out (alu_bus_nibble_out),
.i_push (push),
.i_pop (pop),
.i_alu_debug (alu_debug),
.o_alu_stall_dec (alu_stalls_dec),
.i_ins_decoded (ins_decoded),
.i_field_start (field_start),
.i_field_last (field_last),
.i_imm_value (imm_value),
.i_alu_op (alu_op),
.i_alu_no_stall (alu_no_stall),
.i_reg_dest (reg_dest),
.i_reg_src1 (reg_src1),
.i_reg_src2 (reg_src2),
.i_ins_alu_op (ins_alu_op),
.i_ins_test_go (ins_test_go),
.i_ins_set_mode (ins_set_mode),
.i_ins_rtn (ins_rtn),
.i_ins_config (ins_config),
.i_ins_mem_xfr (ins_mem_xfr),
.i_xfr_dir_out (xfr_dir_out),
.i_ins_unconfig (ins_unconfig),
.i_mode_dec (mode_dec),
.i_set_xm (set_xm),
.i_set_carry (set_carry),
.i_test_carry (test_carry),
.i_carry_val (carry_val),
.o_reg_p (reg_p),
.o_pc (reg_pc)
);
// interconnections
wire [19:0] alu_bus_address;
wire [3:0] alu_bus_xfr_cnt;
wire [0:0] alu_bus_pc_read;
wire [0:0] alu_bus_dp_read;
wire [0:0] alu_bus_dp_write;
wire [0:0] alu_bus_load_pc;
wire [0:0] alu_bus_load_dp;
wire [0:0] alu_bus_config;
wire [3:0] ctrl_bus_nibble_in;
wire [3:0] alu_bus_nibble_out;
wire [0:0] alu_stalls_dec;
wire [3:0] reg_p;
wire [19:0] reg_pc;
/*
*
* Bus controller module
*
*/
saturn_bus_ctrl m_bus_ctrl (
// basic stuff
.i_clk (i_clk),
.i_reset (i_reset),
.i_phase (o_phase),
.i_cycle_ctr (cycle_ctr),
.i_stalled (mem_ctrl_stall),
.i_alu_busy (dec_stalled),
.o_stall_alu (bus_stalls_core),
//bus i/o
.o_bus_reset (o_bus_reset),
.i_bus_data (i_bus_data_in),
.o_bus_data (o_bus_data_out),
.o_bus_strobe (o_bus_strobe),
.o_bus_cmd_data (o_bus_cmd_data),
// interface to the rest of the machine
.i_alu_pc (reg_pc),
.i_address (alu_bus_address),
.i_cmd_load_pc (alu_bus_load_pc),
.i_cmd_load_dp (alu_bus_load_dp),
.i_read_pc (alu_bus_pc_read),
.i_cmd_dp_read (alu_bus_dp_read),
.i_cmd_dp_write (alu_bus_dp_write),
.i_cmd_reset (ins_reset),
.i_cmd_config (alu_bus_config),
.i_mem_xfr (ins_mem_xfr),
.i_xfr_out (xfr_dir_out),
.i_xfr_cnt (alu_bus_xfr_cnt),
.i_nibble (alu_bus_nibble_out),
.o_nibble (ctrl_bus_nibble_in)
);
reg [0:0] mem_ctrl_stall;
wire [0:0] bus_stalls_core;
// `define DEBUG_CLOCKS
initial begin
clk_phase = 0;
ck_debugger = 0; // phase 0
ck_bus_send = 0; // phase 0
ck_bus_recv = 0; // phase 1
ck_bus_ecmd = 0; // phase 3
ck_inst_dec = 0; // phase 2
ck_inst_exe = 0; // phase 3
ck_alu_dump = 0;
ck_alu_prep = 0; // phase 1
ck_alu_calc = 0; // phase 2
ck_alu_init = 0; // phase 0
ck_alu_save = 0; // phase 3
clock_end = 0;
cycle_ctr = 0;
mem_ctrl_stall = 0;
`ifdef DEBUG_CLOCKS
$monitor("RST %b | CLK %b | CLKP %d | CYCL %d | PC %5h | eRST %b | eDBG %b | eBSND %b | eBRECV %b | eAPR %b | eACALC %b | eINDC %b | eASAVE %b | eINDX %b",
reset, clk, clk_phase, cycle_ctr, reg_pc, en_reset,
en_debugger, en_bus_send,
en_bus_recv, en_alu_prep,
en_alu_calc, en_inst_dec,
en_alu_save, en_inst_exec);
`endif
end
//--------------------------------------------------------------------------------------------------
//
// clock generation
//
//--------------------------------------------------------------------------------------------------
always @(posedge i_clk) begin
if (!i_reset) begin
clk_phase <= clk_phase + 1;
ck_debugger <= clk_phase[1:0] == `PH_DEBUGGER;
ck_bus_send <= clk_phase[1:0] == `PH_BUS_SEND;
ck_bus_recv <= clk_phase[1:0] == `PH_BUS_RECV;
ck_bus_ecmd <= clk_phase[1:0] == `PH_BUS_ECMD;
ck_inst_dec <= clk_phase[1:0] == `PH_INST_DEC;
ck_inst_exe <= clk_phase[1:0] == `PH_INST_EXE;
ck_alu_dump <= clk_phase[1:0] == `PH_ALU_DUMP;
ck_alu_init <= clk_phase[1:0] == `PH_ALU_INIT;
ck_alu_prep <= clk_phase[1:0] == `PH_ALU_PREP;
ck_alu_calc <= clk_phase[1:0] == `PH_ALU_CALC;
ck_alu_save <= clk_phase[1:0] == `PH_ALU_SAVE;
cycle_ctr <= cycle_ctr + { {31{1'b0}}, (clk_phase[1:0] == `PH_BUS_SEND) };
if (cycle_ctr == (max_cycle + 1)) begin
$display(".-----------------------------.");
$display("| OUT OF CYCLES %d |", cycle_ctr);
$display("`-----------------------------´");
clock_end <= 1;
end
end else begin
clk_phase <= ~0;
ck_debugger <= 0;
ck_bus_send <= 0;
ck_bus_recv <= 0;
ck_bus_ecmd <= 0;
ck_inst_dec <= 0;
ck_inst_exe <= 0;
ck_alu_dump <= 0;
ck_alu_init <= 0;
ck_alu_prep <= 0;
ck_alu_calc <= 0;
ck_alu_save <= 0;
clock_end <= 0;
cycle_ctr <= ~0;
max_cycle <= 72;
mem_ctrl_stall <= 0;
`ifndef SIM
led[7:0] <= reg_pc[7:0];
`endif
end
end
//--------------------------------------------------------------------------------------------------
//
// test cases
//
//--------------------------------------------------------------------------------------------------
wire dec_stalled;
wire alu_stalled;
assign dec_stalled = alu_stalls_dec || bus_stalls_core;
assign alu_stalled = bus_stalls_core;
`ifdef SIM
assign o_halt = clock_end || inv_opcode;
`endif
// Verilator lint_off UNUSED
//wire [N-1:0] unused;
//assign unused = { };
// Verilator lint_on UNUSED
endmodule
`ifdef SIM
`include "def-buscmd.v"
/******************************************************************************
*
* test rom
*
****************************************************************************/
module test_rom (
i_phase,
i_reset,
i_bus_data_in,
o_bus_data_out,
i_bus_strobe,
i_bus_cmd_data
);
input wire [1:0] i_phase;
input wire [0:0] i_reset;
input wire [3:0] i_bus_data_in;
output reg [3:0] o_bus_data_out;
input wire [0:0] i_bus_strobe;
input wire [0:0] i_bus_cmd_data;
reg [31:0] cycles;
`ifdef SIM
`define ROMBITS 20
`else
`define ROMBITS 12
`endif
reg [3:0] rom [0:2**`ROMBITS-1];
reg [19:0] local_pc;
reg [19:0] local_dp;
reg [3:0] last_bus_cmd;
reg [2:0] addr_c;
wire [0:0] s_load_pc;
wire [0:0] s_load_dp;
wire [0:0] s_pc_read;
wire [0:0] s_dp_read;
wire [0:0] s_dp_write;
assign s_load_pc = (last_bus_cmd == `BUSCMD_LOAD_PC);
assign s_load_dp = (last_bus_cmd == `BUSCMD_LOAD_DP);
assign s_pc_read = (last_bus_cmd == `BUSCMD_PC_READ);
assign s_dp_read = (last_bus_cmd == `BUSCMD_DP_READ);
assign s_dp_write = (last_bus_cmd == `BUSCMD_DP_WRITE);
initial begin
$readmemh("rom-gx-r.hex", rom, 0, 2**`ROMBITS-1);
// $monitor("rst %b | strb %b | c/d %b | bus_i %h | bus_o %h | last %h | slpc %b | addr_c %0d | lpc %5h | ldp %5h",
// i_reset, i_bus_strobe, i_bus_cmd_data, i_bus_data_in, o_bus_data_out,
// last_bus_cmd, s_load_pc, addr_c, local_pc, local_dp);
end
always @(posedge i_bus_strobe) begin
if (i_reset) begin
cycles <= 0;
last_bus_cmd <= `BUSCMD_NOP;
addr_c <= 0;
local_pc <= 0;
local_dp <= 0;
end
if (!i_reset)
cycles <= cycles + 1;
if (!i_bus_cmd_data) begin
$write("ROM %0d: [%d] COMMAND ", i_phase, cycles);
case (i_bus_data_in)
`BUSCMD_PC_READ: $write("PC_READ"); // 2
`BUSCMD_DP_WRITE: $write("DP_WRITE"); // 5
`BUSCMD_LOAD_PC: $write("LOAD_PC"); // 6
`BUSCMD_LOAD_DP: $write("LOAD_DP"); // 7
`BUSCMD_CONFIGURE: $write("CONFIGURE (ignore)"); // 8
`BUSCMD_RESET: $write("RESET (ignore)"); // 15
endcase
$write(" (%h)\n", i_bus_data_in);
last_bus_cmd <= i_bus_data_in;
end
// if (i_bus_cmd_data) begin
// $display("BUS DATA %h", i_bus_data_in);
// end
if (i_bus_cmd_data && s_load_pc) begin
$display("ROM %0d: [%d] ADDR_IN(%0d) %h => PC [%5h]", i_phase, cycles, addr_c, i_bus_data_in, local_pc);
local_pc[addr_c*4+:4] <= i_bus_data_in;
if (addr_c == 4) $display("ROM : [%d] auto PC_READ [%5h]", cycles, {i_bus_data_in, local_pc[15:0]});
last_bus_cmd <= (addr_c == 4)?`BUSCMD_PC_READ:last_bus_cmd;
addr_c <= (addr_c == 4)?0:addr_c + 1;
end
if (i_bus_cmd_data && s_load_dp) begin
$display("ROM %0d: [%d] ADDR_IN(%0d) %h => DP [%5h]", i_phase, cycles, addr_c, i_bus_data_in, local_dp);
local_dp[addr_c*4+:4] <= i_bus_data_in;
if (addr_c == 4) $display("ROM : [%d] auto DP_READ [%5h]", cycles, {i_bus_data_in, local_dp[15:0]});
last_bus_cmd <= (addr_c == 4)?`BUSCMD_DP_READ:last_bus_cmd;
addr_c <= (addr_c == 4)?0:addr_c + 1;
end
if (i_bus_cmd_data && s_pc_read) begin
o_bus_data_out <= rom[local_pc];
$display("ROM %0d: [%d] %h <= PC_READ [%5h]", i_phase, cycles, rom[local_pc], local_pc);
local_pc <= local_pc + 1;
end
if (i_bus_cmd_data && s_dp_read) begin
o_bus_data_out <= rom[local_dp];
$display("ROM %0d: [%d] %h <= DP_READ [%5h]", i_phase, cycles, rom[local_dp], local_dp);
local_dp <= local_dp + 1;
end
if (i_bus_cmd_data && s_dp_write) begin
$display("ROM %0d: [%d] %h => DP_WRITE [%5h] (ignored)", i_phase, cycles, i_bus_data_in, local_dp);
end
end
endmodule
/******************************************************************************
*
* test harness
*
****************************************************************************/
module saturn_tb;
saturn_core saturn (
.i_clk (clk),
.i_reset (reset),
.o_halt (halt),
.o_bus_reset (core_bus_reset),
.i_bus_data_in (core_bus_data_in),
.o_bus_data_out (core_bus_data_out),
.o_bus_strobe (core_bus_strobe),
.o_bus_cmd_data (core_bus_cmd_data),
.o_phase (core_phase)
);
test_rom rom (
.i_phase (core_phase),
.i_reset (core_bus_reset),
.i_bus_data_in (core_bus_data_out),
.o_bus_data_out (core_bus_data_in),
.i_bus_strobe (core_bus_strobe),
.i_bus_cmd_data (core_bus_cmd_data)
);
reg [0:0] clk;
reg [0:0] reset;
wire [0:0] halt;
wire [0:0] core_bus_reset;
wire [3:0] core_bus_data_in;
wire [3:0] core_bus_data_out;
wire [0:0] core_bus_strobe;
wire [0:0] core_bus_cmd_data;
wire [1:0] core_phase;
always
#10 clk = (clk === 1'b0);
initial begin
// $monitor ("c %b | r %b | in %h | out %h | str %b | cd %b",
// clk, reset, core_bus_data_in, core_bus_data_out, core_bus_strobe, core_bus_cmd_data);
end
initial begin
$display("starting the simulation");
clk <= 0;
reset <= 1;
@(posedge clk);
@(posedge clk);
@(posedge clk);
reset <= 0;
@(posedge halt);
$finish;
end
endmodule
`endif