hp-saturn/saturn_core.v

504 lines
11 KiB
Coq
Raw Normal View History

2019-02-06 10:40:55 +01:00
/*
* Licence: GPLv3 or later
*/
2019-02-04 20:36:47 +01:00
`default_nettype none //
`include "bus_commands.v"
2019-02-09 00:01:48 +01:00
`include "hp48_00_bus.v"
2019-02-04 15:02:33 +01:00
/**************************************************************************************************
*
*
2019-02-05 08:49:14 +01:00
*
*
*
*/
2019-02-06 10:40:55 +01:00
2019-02-04 17:14:08 +01:00
`ifdef SIM
2019-02-04 18:17:14 +01:00
module saturn_core (
2019-02-04 15:02:33 +01:00
input clk,
input reset,
output halt,
2019-02-07 22:54:06 +01:00
output [3:0] busstate,
2019-02-09 19:18:58 +01:00
output [11:0] decstate
2019-02-04 18:17:14 +01:00
);
2019-02-04 17:46:29 +01:00
`else
2019-02-04 18:17:14 +01:00
module saturn_core (
input clk_25mhz,
2019-02-10 12:04:53 +01:00
input [ 6:0] btn,
// output wifi_gpio0,
2019-02-04 22:08:17 +01:00
output [7:0] led
2019-02-04 09:59:35 +01:00
);
2019-02-04 22:08:17 +01:00
wire clk;
wire reset;
2019-02-07 22:54:06 +01:00
reg clk2;
2019-02-04 18:17:14 +01:00
2019-02-10 12:04:53 +01:00
// assign wifi_gpio0 = 1'b1;
2019-02-04 18:17:14 +01:00
assign clk = clk_25mhz;
assign reset = btn[1];
`endif
2019-02-04 09:59:35 +01:00
2019-02-05 07:07:19 +01:00
// data transfer constants
2019-02-08 19:09:13 +01:00
2019-02-05 07:07:19 +01:00
2019-02-07 22:54:06 +01:00
// clocks
reg clk2;
reg clk3;
wire bus_ctrl_clk;
wire ph0;
wire ph1;
wire ph2;
wire ph3;
reg en_bus_clk;
wire bus_strobe;
reg en_dec_clk;
wire dec_strobe;
2019-02-04 09:59:35 +01:00
// state machine stuff
2019-02-07 22:54:06 +01:00
wire halt;
reg [31:0] cycle_ctr;
2019-02-08 12:46:32 +01:00
reg [31:0] instr_ctr;
2019-02-07 22:54:06 +01:00
reg decode_error;
reg debug_stop;
2019-02-08 19:09:13 +01:00
reg [3:0] cycle_type;
reg [3:0] next_cycle;
2019-02-07 22:54:06 +01:00
reg read_next_pc;
reg execute_cycle;
reg inc_pc;
2019-02-07 22:54:06 +01:00
reg read_nibble;
reg first_nibble;
2019-02-09 19:18:58 +01:00
reg [11:0] decstate;
reg [3:0] regdump;
2019-02-04 09:59:35 +01:00
// bus access
reg [19:0] bus_address;
2019-02-06 10:40:55 +01:00
reg [3:0] bus_command;
reg [3:0] bus_nibble_in;
wire [3:0] bus_nibble_out;
wire bus_error;
reg bus_load_pc;
2019-02-08 11:47:06 +01:00
reg en_bus_load_pc;
// should go away, the rom should work like any other bus module
reg rom_enable;
2019-02-05 08:49:14 +01:00
2019-02-04 09:59:35 +01:00
// internal registers
reg [19:0] new_PC;
2019-02-08 11:47:06 +01:00
reg [19:0] next_PC;
2019-02-08 11:15:16 +01:00
reg [19:0] inst_start_PC;
2019-02-09 19:18:58 +01:00
2019-02-04 09:59:35 +01:00
reg [2:0] rstk_ptr;
2019-02-09 19:18:58 +01:00
2019-02-04 09:59:35 +01:00
reg [19:0] jump_base;
reg [19:0] jump_offset;
2019-02-09 19:18:58 +01:00
2019-02-04 15:02:33 +01:00
reg hex_dec;
2019-02-07 22:54:06 +01:00
`define MODE_HEX 0;
`define MODE_DEC 1;
2019-02-05 07:07:19 +01:00
// data transfer registers
reg [3:0] t_offset;
reg [3:0] t_cnt;
reg [3:0] t_ctr;
reg t_dir;
reg t_ptr;
reg t_reg;
2019-02-10 12:04:53 +01:00
reg t_ftype;
2019-02-05 07:07:19 +01:00
reg [3:0] t_field;
2019-02-04 09:59:35 +01:00
2019-02-09 19:18:58 +01:00
reg [3:0] nb_in;
reg [3:0] nb_out;
reg [19:0] add_out;
2019-02-10 12:04:53 +01:00
// temporary stuff
reg t_set_test;
reg t_set_test_val;
2019-02-10 13:39:56 +01:00
reg t_add_sub;
2019-02-10 12:04:53 +01:00
2019-02-04 09:59:35 +01:00
// processor registers
reg [19:0] PC;
reg [3:0] P;
reg [15:0] ST;
reg [3:0] HST;
2019-02-04 15:02:33 +01:00
reg Carry;
2019-02-04 09:59:35 +01:00
reg [19:0] RSTK[0:7];
reg [19:0] D0;
reg [19:0] D1;
reg [63:0] A;
reg [63:0] B;
reg [63:0] C;
reg [63:0] D;
reg [63:0] R0;
reg [63:0] R1;
reg [63:0] R2;
reg [63:0] R3;
reg [63:0] R4;
hp48_bus bus_ctrl (
2019-02-07 22:54:06 +01:00
.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)
2019-02-05 08:49:14 +01:00
);
2019-02-07 22:54:06 +01:00
initial
begin
$display("initializing clocks");
clk2 = 0;
clk3 = 0;
en_bus_clk = 0;
$display("initialize cycle counter");
cycle_ctr = -1;
2019-02-08 12:46:32 +01:00
instr_ctr = 0;
2019-02-07 22:54:06 +01:00
$display("initializing bus_command");
bus_command = `BUSCMD_NOP;
$display("initializing busstate");
2019-02-08 19:09:13 +01:00
next_cycle = `BUSCMD_LOAD_PC;
2019-02-07 22:54:06 +01:00
$display("Initializing decstate");
decstate = 0;
$display("initializing control bits");
decode_error = 0;
debug_stop = 0;
2019-02-07 22:54:06 +01:00
bus_load_pc = 1;
2019-02-08 11:47:06 +01:00
en_bus_load_pc = 1;
2019-02-07 22:54:06 +01:00
read_next_pc = 1;
execute_cycle = 0;
inc_pc = 0;
2019-02-07 22:54:06 +01:00
$display("should be initializing registers");
2019-02-08 00:02:55 +01:00
hex_dec = `MODE_HEX;
2019-02-07 22:54:06 +01:00
PC = 0;
new_PC = 0;
2019-02-08 11:47:06 +01:00
next_PC = 0;
2019-02-08 11:15:16 +01:00
inst_start_PC = 0;
2019-02-08 00:02:55 +01:00
rstk_ptr = 7;
2019-02-07 22:54:06 +01:00
// $monitor("rst %b | CLK %b | CLK2 %b | CLK3 %b | PH0 %b | PH1 %b | PH2 %b | PH3 %b | CTR %d | EBCLK %b| STRB %b | BLPC %b | bnbi %b | bnbo %b | nb %b ",
// reset, clk, clk2, clk3, ph0, ph1, ph2, ph3, cycle_ctr, en_bus_clk, strobe, bus_load_pc, bus_nibble_in, bus_nibble_out, nibble);
// $monitor("CTR %d | EBCLK %b| B_STRB %b | EDCLK %b | D_STRB %b | BLPC %b | bnbi %b | bnbo %b | nb %b ",
// cycle_ctr, en_bus_clk, bus_strobe, en_dec_clk, dec_strobe, bus_load_pc, bus_nibble_in, bus_nibble_out, nibble);
2019-02-08 11:47:06 +01:00
// $monitor("BLPC %b | EBLPC %b",
// bus_load_pc, en_bus_load_pc);
2019-02-09 00:01:48 +01:00
//$monitor("NC %h", next_cycle);
2019-02-07 22:54:06 +01:00
end
2019-02-07 22:54:06 +01:00
//--------------------------------------------------------------------------------------------------
//
// clock generation
//
//--------------------------------------------------------------------------------------------------
2019-02-04 17:46:29 +01:00
2019-02-04 09:59:35 +01:00
always @(posedge clk)
2019-02-08 12:46:32 +01:00
if (!reset) clk2 <= !clk2;
2019-02-07 22:54:06 +01:00
always @(negedge clk)
2019-02-08 12:46:32 +01:00
clk3 <= !clk3 | reset;
2019-02-04 09:59:35 +01:00
2019-02-08 12:46:32 +01:00
assign bus_ctrl_clk = clk & !reset;
assign ph0 = clk & clk3 & !reset;
assign ph1 = !clk & !clk2 & !reset;
assign ph2 = clk & !clk3 & !reset;
assign ph3 = !clk & clk2 & !reset;
2019-02-07 22:54:06 +01:00
assign bus_strobe = ph1 & en_bus_clk;
assign dec_strobe = ph3 & en_dec_clk;
2019-02-04 09:59:35 +01:00
//--------------------------------------------------------------------------------------------------
//
2019-02-07 22:54:06 +01:00
// bus control
2019-02-04 09:59:35 +01:00
//
//--------------------------------------------------------------------------------------------------
2019-02-08 21:11:47 +01:00
`include "fields.v"
// `include "bus_commands.v"
2019-02-07 22:54:06 +01:00
always @(posedge bus_ctrl_clk)
begin
2019-02-08 12:46:32 +01:00
if (!reset) begin
2019-02-07 22:54:06 +01:00
if (clk3) begin
en_dec_clk <= 0;
cycle_ctr <= cycle_ctr + 1;
2019-02-08 19:09:13 +01:00
case (next_cycle)
`BUSCMD_NOP: begin
bus_command <= `BUSCMD_NOP;
$display("BUS NOT READING, STILL CLOCKING");
end
2019-02-08 21:11:47 +01:00
`BUSCMD_PC_READ: begin
bus_command <= `BUSCMD_PC_READ;
en_bus_clk <= 1;
PC <= next_PC;
inc_pc <= 1;
end
2019-02-09 19:18:58 +01:00
`BUSCMD_DP_READ: begin
bus_command <= `BUSCMD_DP_READ;
en_bus_clk <= 1;
end
2019-02-08 21:11:47 +01:00
`BUSCMD_DP_WRITE: begin
bus_command <= `BUSCMD_DP_WRITE;
2019-02-09 19:18:58 +01:00
bus_nibble_in <= nb_out;
2019-02-08 21:11:47 +01:00
en_bus_clk <= 1;
end
2019-02-08 19:09:13 +01:00
`BUSCMD_LOAD_PC: begin
bus_command <= `BUSCMD_LOAD_PC;
bus_address <= new_PC;
2019-02-08 11:47:06 +01:00
next_PC <= new_PC;
PC <= new_PC;
2019-02-07 22:54:06 +01:00
en_bus_clk <= 1;
end
2019-02-08 21:11:47 +01:00
`BUSCMD_LOAD_DP: begin
bus_command <= `BUSCMD_LOAD_DP;
2019-02-09 19:18:58 +01:00
bus_address <= add_out;
2019-02-08 21:11:47 +01:00
en_bus_clk <= 1;
end
`BUSCMD_CONFIGURE: begin
bus_command <= `BUSCMD_CONFIGURE;
2019-02-09 19:18:58 +01:00
bus_address <= add_out;
2019-02-08 19:09:13 +01:00
en_bus_clk <= 1;
2019-02-08 21:11:47 +01:00
end
2019-02-09 00:01:48 +01:00
`BUSCMD_RESET: begin
bus_command <= `BUSCMD_RESET;
en_bus_clk <= 1;
end
2019-02-08 21:11:47 +01:00
default: begin
$display("BUS PHASE 1: %h UNIMPLEMENTED", next_cycle);
2019-02-08 19:09:13 +01:00
end
endcase
2019-02-04 15:02:33 +01:00
end
2019-02-07 22:54:06 +01:00
else begin
2019-02-08 19:09:13 +01:00
case (next_cycle)
`BUSCMD_NOP: begin
en_dec_clk <= 1;
end
`BUSCMD_PC_READ: begin
2019-02-09 19:18:58 +01:00
nb_in <= bus_nibble_out;
2019-02-08 19:09:13 +01:00
en_dec_clk <= 1;
if (inc_pc) begin
next_PC <= PC + 1;
inc_pc <= 0;
end
// $display("reading nibble %h", bus_nibble_out);
end
2019-02-09 19:18:58 +01:00
`BUSCMD_DP_READ: begin
nb_in <= bus_nibble_out;
en_dec_clk <= 1;
end
2019-02-08 21:11:47 +01:00
`BUSCMD_DP_WRITE: begin
// $display("BUS PHASE 2: DP_WRITE cnt %h | ctr %h", t_cnt, t_ctr);
en_dec_clk <= 1;
end
`BUSCMD_LOAD_PC: begin
$display("CYCLE %d | INSTR %d -> BUSCMD_LOAD_PC %5h", cycle_ctr, instr_ctr, new_PC);
en_dec_clk <= 1;
end
`BUSCMD_LOAD_DP: begin
$display("CYCLE %d | INSTR %d -> BUSCMD_LOAD_DP %s %5h",
2019-02-09 19:18:58 +01:00
cycle_ctr, instr_ctr, t_ptr?"D1":"D0", add_out);
2019-02-08 21:11:47 +01:00
en_dec_clk <= 1;
end
`BUSCMD_CONFIGURE: begin
2019-02-09 19:18:58 +01:00
$display("CYCLE %d | INSTR %d -> BUSCMD_CONFIGURE %5h", cycle_ctr, instr_ctr, add_out);
2019-02-08 21:11:47 +01:00
en_dec_clk <= 1;
end
2019-02-09 00:01:48 +01:00
`BUSCMD_RESET: begin
$display("CYCLE %d | INSTR %d -> BUSCMD_RESET", cycle_ctr, instr_ctr);
en_dec_clk <= 1;
end
2019-02-08 21:11:47 +01:00
default: begin
$display("BUS PHASE 2: %h UNIMPLEMENTED", next_cycle);
end
2019-02-08 19:09:13 +01:00
endcase
2019-02-07 22:54:06 +01:00
en_bus_clk <= 0;
end
2019-02-07 22:54:06 +01:00
end
else begin
$display("RESET");
end
end
2019-02-07 22:54:06 +01:00
always @(posedge ph1)
begin
end
2019-02-07 22:54:06 +01:00
always @(posedge ph2)
begin
end
2019-02-04 15:02:33 +01:00
2019-02-07 22:54:06 +01:00
always @(posedge ph3) begin
2019-02-10 12:47:50 +01:00
if (cycle_ctr == 450)
debug_stop <= 1;
2019-02-07 22:54:06 +01:00
end
2019-02-04 09:59:35 +01:00
//--------------------------------------------------------------------------------------------------
//
2019-02-07 22:54:06 +01:00
// instruction decoder
2019-02-04 09:59:35 +01:00
//
//--------------------------------------------------------------------------------------------------
2019-02-07 22:54:06 +01:00
`include "decstates.v"
2019-02-04 15:02:33 +01:00
2019-02-07 22:54:06 +01:00
always @(posedge dec_strobe) begin
2019-02-08 19:09:13 +01:00
if ((next_cycle == `BUSCMD_LOAD_PC)|
2019-02-08 21:11:47 +01:00
(next_cycle == `BUSCMD_CONFIGURE)|
2019-02-09 00:01:48 +01:00
(next_cycle == `BUSCMD_RESET)|
2019-02-08 19:09:13 +01:00
((next_cycle == `BUSCMD_NOP)&(decstate == `DEC_START))) begin
$display("SETTING next_cycle to BUSCMD_PC_READ");
next_cycle <= `BUSCMD_PC_READ;
end else begin
`ifdef SIM
2019-02-09 19:18:58 +01:00
if ((decstate == `DEC_START)|(decstate == `DEC_TEST_GO)) begin
2019-02-08 19:09:13 +01:00
// display registers
$display("PC: %05h Carry: %b h: %s rp: %h RSTK7: %05h", PC, Carry, hex_dec?"DEC":"HEX", rstk_ptr, RSTK[7]);
$display("P: %h HST: %b ST: %b RSTK6: %5h", P, HST, ST, RSTK[6]);
$display("A: %h R0: %h RSTK5: %5h", A, R0, RSTK[5]);
$display("B: %h R1: %h RSTK4: %5h", B, R1, RSTK[4]);
$display("C: %h R2: %h RSTK3: %5h", C, R2, RSTK[3]);
$display("D: %h R3: %h RSTK2: %5h", D, R3, RSTK[2]);
$display("D0: %h D1: %h R4: %h RSTK1: %5h", D0, D1, R4, RSTK[1]);
$display(" RSTK0: %5h", RSTK[0]);
end
`endif
2019-02-09 19:18:58 +01:00
$display("CYCLE %d | INSTR %d | PC %h | DECSTATE %3h | NIBBLE %h",
2019-02-08 19:09:13 +01:00
cycle_ctr,
(decstate == `DEC_START)?instr_ctr+1:instr_ctr,
2019-02-09 19:18:58 +01:00
PC, decstate, nb_in);
2019-02-08 19:09:13 +01:00
case (decstate)
`DEC_START: begin
instr_ctr <= instr_ctr + 1;
inst_start_PC <= PC;
2019-02-09 19:18:58 +01:00
case (nb_in)
2019-02-08 19:09:13 +01:00
4'h0: decstate <= `DEC_0X;
4'h1: decstate <= `DEC_1X;
4'h2: decstate <= `DEC_P_EQ_N;
4'h3: decstate <= `DEC_LC_LEN;
4'h6: decstate <= `DEC_GOTO;
2019-02-09 00:01:48 +01:00
4'h7: decstate <= `DEC_GOSUB;
2019-02-08 19:09:13 +01:00
4'h8: decstate <= `DEC_8X;
4'hA: decstate <= `DEC_AX;
2019-02-09 00:01:48 +01:00
4'hB: decstate <= `DEC_BX;
2019-02-09 11:53:45 +01:00
4'hC: decstate <= `DEC_CX;
2019-02-09 00:01:48 +01:00
4'hD: decstate <= `DEC_DX;
2019-02-09 11:53:45 +01:00
4'hF: decstate <= `DEC_FX;
2019-02-08 19:09:13 +01:00
default: begin
$display("ERROR : DEC_START");
decode_error <= 1;
end
endcase
end
2019-02-08 00:02:55 +01:00
`include "opcodes/0x.v"
2019-02-08 19:09:13 +01:00
`include "opcodes/1x.v"
2019-02-09 19:18:58 +01:00
`include "opcodes/13x_ptr_and_AC.v"
2019-02-08 19:09:13 +01:00
`include "opcodes/1[45]_memaccess.v"
2019-02-10 13:39:56 +01:00
`include "opcodes/1[678C]n_D[01]_math_n.v"
2019-02-10 12:47:50 +01:00
`include "opcodes/1[ABEF]nnnnn_D[01]_EQ_5n.v"
2019-02-07 22:54:06 +01:00
`include "opcodes/2n_P_EQ_n.v"
`include "opcodes/3n[x...]_LC.v"
2019-02-07 22:54:06 +01:00
`include "opcodes/6xxx_GOTO.v"
2019-02-09 00:01:48 +01:00
`include "opcodes/7xxx_GOSUB.v"
2019-02-07 22:54:06 +01:00
`include "opcodes/8x.v"
`include "opcodes/80x.v"
2019-02-10 12:04:53 +01:00
`include "opcodes/808x.v"
`include "opcodes/808[4-B]_[AC]BIT_set_test.v"
2019-02-10 09:02:24 +01:00
`include "opcodes/80[CD]n_C_and_P_n.v"
2019-02-08 00:02:55 +01:00
`include "opcodes/82x_CLRHST.v"
2019-02-10 09:02:24 +01:00
`include "opcodes/8[4567]n_work_test_ST.v"
`include "opcodes/8[89]n_test_P.v"
2019-02-09 19:18:58 +01:00
`include "opcodes/8Ax_test_[n]eq_A.v"
2019-02-07 22:54:06 +01:00
`include "opcodes/8[DF]xxxxx_GO.v"
2019-02-08 19:09:13 +01:00
`include "opcodes/A[ab]x.v"
`include "opcodes/Bx_math_ops_shift.v"
2019-02-09 11:53:45 +01:00
`include "opcodes/Cx.v"
2019-02-09 00:01:48 +01:00
`include "opcodes/Dx_regs_field_A.v"
2019-02-09 11:53:45 +01:00
`include "opcodes/Fx.v"
2019-02-10 09:02:24 +01:00
`include "opcodes/xx_RTNYES_GOYES.v"
2019-02-08 19:09:13 +01:00
default: begin
$display("ERROR : GENERAL");
decode_error <= 1;
end
endcase
end
2019-02-07 22:54:06 +01:00
end
2019-02-04 22:08:17 +01:00
2019-02-07 22:54:06 +01:00
//--------------------------------------------------------------------------------------------------
//
// dump all registers on leds
//
//--------------------------------------------------------------------------------------------------
2019-02-04 22:08:17 +01:00
2019-02-04 23:51:36 +01:00
`ifndef SIM
2019-02-04 22:08:17 +01:00
2019-02-07 22:54:06 +01:00
`define REGDMP_HEX 0
always @(negedge clk)
begin
2019-02-04 22:08:17 +01:00
case (regdump)
2019-02-07 22:54:06 +01:00
`REGDMP_HEX: led <= {7'b0000000, hex_dec};
2019-02-04 22:08:17 +01:00
default: led <= 8'b11111111;
endcase
regdump <= regdump + 1;
2019-02-07 22:54:06 +01:00
if (reset)
regdump <= `REGDMP_HEX;
end
2019-02-04 23:51:36 +01:00
`endif
2019-02-04 22:08:17 +01:00
assign halt = bus_error | decode_error | debug_stop;
2019-02-07 22:54:06 +01:00
2019-02-04 22:08:17 +01:00
// Verilator lint_off UNUSED
//wire [N-1:0] unused;
//assign unused = { };
// Verilator lint_on UNUSED
2019-02-04 09:59:35 +01:00
endmodule
`ifdef SIM
2019-02-04 17:46:29 +01:00
module saturn_tb;
2019-02-04 15:02:33 +01:00
reg clk;
reg reset;
2019-02-04 09:59:35 +01:00
wire halt;
2019-02-07 22:54:06 +01:00
wire [3:0] busstate;
2019-02-09 19:18:58 +01:00
wire [11:0] decstate;
2019-02-04 09:59:35 +01:00
saturn_core saturn (
2019-02-04 15:02:33 +01:00
.clk (clk),
.reset (reset),
.halt (halt),
2019-02-07 22:54:06 +01:00
.busstate (busstate),
2019-02-04 15:02:33 +01:00
.decstate (decstate)
2019-02-04 09:59:35 +01:00
);
always
#10 clk = (clk === 1'b0);
initial begin
2019-02-04 15:02:33 +01:00
//$monitor ("c %b | r %b | run %h | dec %h", clk, reset, runstate, decstate);
2019-02-04 09:59:35 +01:00
end
initial begin
2019-02-04 15:02:33 +01:00
$display("starting the simulation");
2019-02-04 09:59:35 +01:00
clk <= 0;
reset <= 1;
@(posedge clk);
2019-02-07 22:54:06 +01:00
@(posedge clk);
@(posedge clk);
2019-02-04 09:59:35 +01:00
reset <= 0;
@(posedge halt);
$finish;
end
endmodule
2019-02-04 17:46:29 +01:00
`else
2019-02-04 09:59:35 +01:00
`endif