mirror of
https://github.com/sxpert/hp-saturn
synced 2024-11-16 19:50:19 +01:00
1291 lines
No EOL
26 KiB
Verilog
1291 lines
No EOL
26 KiB
Verilog
/*
|
|
* Licence: GPLv3 or later
|
|
*/
|
|
|
|
`default_nettype none //
|
|
|
|
`include "bus_commands.v"
|
|
`include "hp48_bus.v"
|
|
|
|
/**************************************************************************************************
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
/****************************
|
|
*
|
|
* runstate
|
|
*
|
|
*/
|
|
|
|
`define NEXT_INSTR 0
|
|
`define INSTR_START 1
|
|
`define INSTR_STROBE 2
|
|
`define INSTR_READY 3
|
|
|
|
`define READ_START 4
|
|
`define READ_STROBE 5
|
|
`define READ_DONE 6
|
|
`define READ_VALUE 7
|
|
|
|
`define WRITE_START 8
|
|
`define WRITE_STROBE 9
|
|
`define WRITE_DONE 10
|
|
|
|
`define RUN_DECODE 12
|
|
`define RUN_EXEC 13
|
|
|
|
`define RUN_INIT 15
|
|
|
|
/****************************
|
|
*
|
|
* runstate
|
|
*
|
|
*/
|
|
|
|
|
|
|
|
`ifdef SIM
|
|
module saturn_core (
|
|
input clk,
|
|
input reset,
|
|
output halt,
|
|
output [3:0] runstate,
|
|
output [4:0] decstate
|
|
);
|
|
`else
|
|
module saturn_core (
|
|
input clk_25mhz,
|
|
input [6:0] btn,
|
|
output wifi_gpio0,
|
|
output [7:0] led
|
|
);
|
|
wire clk;
|
|
wire reset;
|
|
|
|
assign wifi_gpio0 = 1'b1;
|
|
assign clk = clk_25mhz;
|
|
assign reset = btn[1];
|
|
|
|
`endif
|
|
|
|
// led display states
|
|
localparam REGDMP_HEX = 0;
|
|
|
|
// instruction decoder states
|
|
|
|
localparam DECODE_START = 0; // x
|
|
|
|
localparam DECODE_0 = 1; // 0
|
|
localparam DECODE_0X = 2; // 0x
|
|
|
|
localparam DECODE_RTNCC = 3; // 03
|
|
localparam DECODE_SETHEX = 4; // 04
|
|
localparam DECODE_SETDEC = 5; // 05
|
|
|
|
localparam DECODE_1 = 6; // 1
|
|
localparam DECODE_1X = 7; // 1x
|
|
localparam DECODE_14 = 8; // 14
|
|
localparam DECODE_15 = 9; // 15
|
|
localparam DECODE_MEMACCESS = 10; // 1[45]x[y]
|
|
localparam DECODE_D0_EQ_5N = 11; // 1Bzyxwv
|
|
|
|
localparam DECODE_P_EQ = 12; // 2n
|
|
|
|
localparam DECODE_LC_LEN = 13; // 3n
|
|
localparam DECODE_LC = 14; // 3n{xxxxxxxxxxxxxxxx}
|
|
|
|
localparam DECODE_GOTO = 15; // 6zyx
|
|
|
|
localparam DECODE_8 = 16; // 8
|
|
localparam DECODE_8X = 17; // 8x
|
|
localparam DECODE_80 = 18; // 80
|
|
|
|
localparam DECODE_CONFIG = 19; // 805
|
|
localparam DECODE_RESET = 20; // 80A
|
|
|
|
localparam DECODE_C_EQ_P_N = 21; // 80Cn
|
|
|
|
localparam DECODE_82 = 22; // 82
|
|
|
|
localparam DECODE_ST_EQ_0_N = 23; // 84n
|
|
localparam DECODE_ST_EQ_1_N = 24; // 85n
|
|
|
|
localparam DECODE_GOVLNG = 25; // 8Dzyxwv
|
|
localparam DECODE_GOSBVL = 26; // 8Fzyxwv
|
|
|
|
localparam DECODE_A = 27; // A
|
|
localparam DECODE_A_FS = 28; // A()
|
|
|
|
// status registers constants
|
|
|
|
localparam HEX = 0;
|
|
localparam DEC = 1;
|
|
|
|
// data transfer constants
|
|
|
|
localparam T_DIR_OUT = 0;
|
|
localparam T_DIR_IN = 1;
|
|
localparam T_PTR_0 = 0;
|
|
localparam T_PTR_1 = 1;
|
|
localparam T_REG_A = 0;
|
|
localparam T_REG_C = 1;
|
|
localparam T_FIELD_P = 0;
|
|
localparam T_FIELD_WP = 1;
|
|
localparam T_FIELD_XS = 2;
|
|
localparam T_FIELD_X = 3;
|
|
localparam T_FIELD_S = 4;
|
|
localparam T_FIELD_M = 5;
|
|
localparam T_FIELD_B = 6;
|
|
localparam T_FIELD_W = 7;
|
|
localparam T_FIELD_LEN = 13;
|
|
localparam T_FIELD_A = 15;
|
|
|
|
// state machine stuff
|
|
reg halt;
|
|
reg [3:0] runstate;
|
|
reg [4:0] decstate;
|
|
reg [3:0] regdump;
|
|
|
|
// bus access
|
|
reg [19:0] bus_address;
|
|
reg [3:0] bus_command;
|
|
reg [3:0] bus_nibble_in;
|
|
wire [3:0] bus_nibble_out;
|
|
wire bus_error;
|
|
reg bus_load_pc;
|
|
|
|
// should go away, the rom should work like any other bus module
|
|
reg rom_enable;
|
|
|
|
// internal registers
|
|
reg [3:0] nibble;
|
|
reg [19:0] saved_PC;
|
|
reg [2:0] rstk_ptr;
|
|
reg [19:0] jump_base;
|
|
reg [19:0] jump_offset;
|
|
reg hex_dec;
|
|
|
|
// 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;
|
|
reg [3:0] t_field;
|
|
|
|
// processor registers
|
|
reg [19:0] PC;
|
|
reg [3:0] P;
|
|
reg [15:0] ST;
|
|
reg [3:0] HST;
|
|
reg Carry;
|
|
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 (
|
|
.clk (clk),
|
|
.reset (reset),
|
|
.address (bus_address),
|
|
.command (bus_command),
|
|
.nibble_in (bus_nibble_in),
|
|
.nibble_out (bus_nibble_out),
|
|
.bus_error (bus_error)
|
|
);
|
|
|
|
|
|
/**************************************************************************************************
|
|
*
|
|
* one single process...
|
|
*
|
|
*/
|
|
|
|
always @(posedge clk)
|
|
begin
|
|
if (reset)
|
|
begin
|
|
// bus
|
|
|
|
bus_command <= `BUSCMD_NOP;
|
|
|
|
// processor state machine
|
|
|
|
halt <= 0;
|
|
runstate <= `RUN_INIT;
|
|
decstate <= DECODE_START;
|
|
regdump <= REGDMP_HEX;
|
|
|
|
// processor registers
|
|
|
|
hex_dec <= HEX;
|
|
rstk_ptr <= 7;
|
|
|
|
PC <= 0;
|
|
saved_PC <= 0;
|
|
P <= 0;
|
|
ST <= 0;
|
|
HST <= 0;
|
|
Carry <= 0;
|
|
RSTK[0] <= 0;
|
|
RSTK[1] <= 0;
|
|
RSTK[2] <= 0;
|
|
RSTK[3] <= 0;
|
|
RSTK[4] <= 0;
|
|
RSTK[5] <= 0;
|
|
RSTK[6] <= 0;
|
|
RSTK[7] <= 0;
|
|
|
|
D0 <= 0;
|
|
D1 <= 0;
|
|
|
|
A <= 0;
|
|
B <= 0;
|
|
C <= 0;
|
|
D <= 0;
|
|
|
|
R0 <= 0;
|
|
R1 <= 0;
|
|
R2 <= 0;
|
|
R3 <= 0;
|
|
R4 <= 0;
|
|
end
|
|
|
|
if (bus_error)
|
|
begin
|
|
halt <= 1;
|
|
end
|
|
|
|
if (runstate == `RUN_INIT)
|
|
begin
|
|
`ifdef SIM
|
|
$display("RUN_INIT => NEXT_INSTR");
|
|
`endif
|
|
bus_load_pc <= 1;
|
|
runstate <= `NEXT_INSTR;
|
|
end
|
|
|
|
/*--------------------------------------------------------------------------------------------------
|
|
*
|
|
* REGISTER UTILITIES
|
|
*
|
|
*------------------------------------------------------------------------------------------------*/
|
|
|
|
// display registers
|
|
`ifdef SIM
|
|
if ((runstate == `NEXT_INSTR) & (~reset))
|
|
begin
|
|
saved_PC <= PC;
|
|
$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
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
//
|
|
// Read from bus
|
|
//
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
|
|
/****
|
|
* Instruction data read
|
|
*
|
|
*
|
|
*/
|
|
|
|
if (runstate == `NEXT_INSTR)
|
|
if (bus_load_pc)
|
|
begin
|
|
`ifdef SIM
|
|
//$display("NEXT_INSTR load PC %5h => INSTR_START", PC);
|
|
`endif
|
|
bus_address <= PC;
|
|
bus_command <= `BUSCMD_LOAD_PC;
|
|
bus_load_pc <= 0;
|
|
runstate <= `INSTR_START;
|
|
end
|
|
else
|
|
begin
|
|
`ifdef SIM
|
|
//$display("NEXT_INSTR => INSTR_STROBE");
|
|
`endif
|
|
bus_command <= `BUSCMD_PC_READ;
|
|
runstate <= `INSTR_STROBE;
|
|
end
|
|
|
|
// start reading instruction
|
|
if (runstate == `INSTR_START)
|
|
begin
|
|
`ifdef SIM
|
|
//$display("INSTR_START => INSTR_STROBE");
|
|
`endif
|
|
bus_command <= `BUSCMD_PC_READ;
|
|
runstate <= `INSTR_STROBE;
|
|
end
|
|
|
|
if (runstate == `INSTR_STROBE)
|
|
begin
|
|
`ifdef SIM
|
|
//$display("INSTR_STROBE => INSTR_READY");
|
|
`endif
|
|
bus_command <= `BUSCMD_NOP;
|
|
nibble <= bus_nibble_out;
|
|
PC <= PC + 1;
|
|
runstate <= `INSTR_READY;
|
|
`ifdef SIM
|
|
//$display("PC: %h | read => %h", PC, bus_nibble_out);
|
|
`endif
|
|
end
|
|
|
|
/****
|
|
*
|
|
* read data from bus
|
|
*
|
|
*/
|
|
|
|
// read from rom clock in
|
|
if (runstate == `READ_STROBE)
|
|
begin
|
|
//$display("READ_STROBE");
|
|
runstate <= `READ_DONE;
|
|
end
|
|
|
|
// read from rom store
|
|
if (runstate == `READ_DONE)
|
|
begin
|
|
//$display("READ_DONE");
|
|
bus_command <= `BUSCMD_NOP;
|
|
nibble <= bus_nibble_out;
|
|
//$display("PC: %h | read => %h", PC, bus_nibble_out);
|
|
PC <= PC + 1;
|
|
// rom_enable <= 1'b0;
|
|
// rom_clock <= 1'b0;
|
|
runstate <= `READ_VALUE;
|
|
end
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
//
|
|
// INSTRUCTION DECODING
|
|
//
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
// first nibble instruction decoder
|
|
if ((runstate == `INSTR_READY) & (decstate == DECODE_START))
|
|
begin
|
|
//$display("`READ_VALUE -> instruction decoder");
|
|
runstate <= `RUN_DECODE;
|
|
case (nibble)
|
|
4'h0 : decstate <= DECODE_0;
|
|
4'h1 : decstate <= DECODE_1;
|
|
4'h2 : decstate <= DECODE_P_EQ;
|
|
4'h3 : decstate <= DECODE_LC_LEN;
|
|
|
|
4'h6 : decstate <= DECODE_GOTO;
|
|
4'h8 : decstate <= DECODE_8;
|
|
4'ha : decstate <= DECODE_A;
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("%05h nibble %h => unimplemented", saved_PC, nibble);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
end
|
|
|
|
/******************************************************************************
|
|
* 0X
|
|
*
|
|
*
|
|
*/
|
|
|
|
case (decstate)
|
|
|
|
//if (decstate == DECODE_0)
|
|
DECODE_0:
|
|
case (runstate)
|
|
`RUN_DECODE: runstate <= `INSTR_START;
|
|
`INSTR_START, `INSTR_STROBE: begin end
|
|
`INSTR_READY:
|
|
case (nibble)
|
|
4'h3: decstate <= DECODE_RTNCC;
|
|
4'h4: decstate <= DECODE_SETHEX;
|
|
4'h5: decstate <= DECODE_SETDEC;
|
|
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("%05h 0%h => unimplemented", saved_PC, nibble);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("DECODE_0 runstate %h", runstate);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
|
|
/******************************************************************************
|
|
* 03 RTNCC
|
|
*
|
|
*
|
|
*/
|
|
|
|
//if (decstate == DECODE_RTNCC)
|
|
DECODE_RTNCC:
|
|
begin
|
|
Carry <= 0;
|
|
PC <= RSTK[rstk_ptr];
|
|
RSTK[rstk_ptr] <= 0;
|
|
rstk_ptr <= rstk_ptr - 1;
|
|
`ifdef SIM
|
|
$display("%05h RTNCC", saved_PC);
|
|
`endif
|
|
bus_load_pc <= 1;
|
|
runstate <= `NEXT_INSTR;
|
|
decstate <= DECODE_START;
|
|
end
|
|
|
|
/******************************************************************************
|
|
* 04 SETHEX
|
|
*
|
|
*
|
|
*/
|
|
|
|
//if (decstate == DECODE_SETHEX)
|
|
DECODE_SETHEX:
|
|
begin
|
|
hex_dec <= HEX;
|
|
`ifdef SIM
|
|
$display("%05h SETHEX", saved_PC);
|
|
`endif
|
|
runstate <= `NEXT_INSTR;
|
|
decstate <= DECODE_START;
|
|
end
|
|
|
|
/******************************************************************************
|
|
* 05 SETDEC
|
|
*
|
|
*
|
|
*/
|
|
|
|
//if (decstate == DECODE_SETDEC)
|
|
DECODE_SETDEC:
|
|
begin
|
|
hex_dec <= DEC;
|
|
`ifdef SIM
|
|
$display("%05h SETDEC", saved_PC);
|
|
`endif
|
|
runstate <= `NEXT_INSTR;
|
|
decstate <= DECODE_START;
|
|
end
|
|
|
|
/******************************************************************************
|
|
* 1X
|
|
*
|
|
*
|
|
*/
|
|
|
|
//if (decstate == DECODE_1)
|
|
DECODE_1:
|
|
case (runstate)
|
|
`RUN_DECODE: runstate <= `INSTR_START;
|
|
`INSTR_START, `INSTR_STROBE: begin end
|
|
`INSTR_READY:
|
|
begin
|
|
case (nibble)
|
|
//4'h4, 4'h5: decode_14_15();
|
|
4'h4: decstate <= DECODE_14;
|
|
4'hb: decstate <= DECODE_D0_EQ_5N;
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("unhandled instruction prefix 1%h", nibble);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
runstate <= `RUN_DECODE;
|
|
end
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("decstate %h", decstate);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
|
|
/******************************************************************************
|
|
* 1[45]
|
|
*
|
|
* ---------- field -----------
|
|
* A B fs d
|
|
* ----------------------------
|
|
* 140 148 150a 158x DAT0=A field 0000 1000
|
|
* 141 149 151a 159x DAT1=A field 0001 1001
|
|
* 142 14A 152a 15Ax A=DAT0 field 0010 1010
|
|
* 143 14B 153a 15Bx A=DAT1 field 0011 1011
|
|
* 144 14C 154a 15Cx DAT0=C field 0100 1100
|
|
* 145 14D 155a 15Dx DAT1=C field 0101 1101
|
|
* 146 14E 156a 15Ex C=DAT0 field 0110 1110
|
|
* 147 14F 157a 15Fx C=DAT1 field 0111 1111
|
|
*
|
|
* fs: P WP XS X S M B W
|
|
* a: 0 1 2 3 4 5 6 7
|
|
*
|
|
* x = d - 1 x = n - 1
|
|
*
|
|
*/
|
|
|
|
//if ((decstate == DECODE_14)|(decstate == DECODE_15))
|
|
DECODE_14, DECODE_15:
|
|
case (runstate)
|
|
`RUN_DECODE: runstate <= `INSTR_START;
|
|
`INSTR_START, `INSTR_STROBE: begin end
|
|
`INSTR_READY:
|
|
case (decstate)
|
|
DECODE_14:
|
|
begin
|
|
`ifdef SIM
|
|
$display("14%h ", nibble);
|
|
`endif
|
|
t_ptr <= nibble[0];
|
|
t_dir <= nibble[1];
|
|
t_reg <= nibble[2];
|
|
if (~nibble[3]) t_field <= T_FIELD_A;
|
|
else t_field <= T_FIELD_B;
|
|
decstate <= DECODE_MEMACCESS;
|
|
runstate <= `RUN_EXEC;
|
|
end
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("15%h UNIMPLEMENTED", nibble);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("DECODE_14_15: runstate %h", decstate);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
|
|
//if (decstate == DECODE_MEMACCESS)
|
|
DECODE_MEMACCESS:
|
|
case (runstate)
|
|
`RUN_EXEC:
|
|
begin
|
|
t_ctr <= 0;
|
|
case (t_field)
|
|
T_FIELD_B:
|
|
begin
|
|
t_offset <= 0;
|
|
t_cnt <= 1;
|
|
end
|
|
endcase
|
|
case (t_dir)
|
|
T_DIR_OUT: runstate <= `WRITE_START;
|
|
T_DIR_IN: runstate <= `READ_START;
|
|
endcase
|
|
`ifdef SIM
|
|
$write("%5h ", saved_PC);
|
|
case (t_dir)
|
|
T_DIR_OUT: $write("%s=%s\t", t_ptr?"DAT1":"DAT0", t_reg?"C":"A");
|
|
T_DIR_IN: $write("%s=%s\t", t_reg?"C":"A", t_ptr?"DAT1":"DAT0");
|
|
endcase
|
|
case (t_field)
|
|
T_FIELD_P: $display("P");
|
|
T_FIELD_WP: $display("WP");
|
|
T_FIELD_XS: $display("XS");
|
|
T_FIELD_X: $display("X");
|
|
T_FIELD_S: $display("S");
|
|
T_FIELD_M: $display("M");
|
|
T_FIELD_B: $display("B");
|
|
T_FIELD_W: $display("W");
|
|
T_FIELD_LEN: $display("%d", t_cnt);
|
|
T_FIELD_A: $display("A");
|
|
endcase
|
|
`endif
|
|
end
|
|
`WRITE_START:
|
|
begin
|
|
`ifdef SIM
|
|
$display("`WRITE_START | ptr %s | dir %s | reg %s | field %h | off %h | ctr %h | cnt %h",
|
|
t_ptr?"D1":"D0", t_dir?"IN":"OUT", t_reg?"C":"A", t_field, t_field, t_offset, t_ctr, t_cnt);
|
|
`endif
|
|
bus_command <= `BUSCMD_LOAD_DP;
|
|
bus_address <= (~t_ptr)?D0:D1;
|
|
runstate <= `WRITE_STROBE;
|
|
end
|
|
`WRITE_STROBE:
|
|
begin
|
|
`ifdef SIM
|
|
$display("`WRITE_STROBE | ptr %s | dir %s | reg %s | field %h | off %h | ctr %h | cnt %h",
|
|
t_ptr?"D1":"D0", t_dir?"IN":"OUT", t_reg?"C":"A", t_field, t_offset, t_ctr, t_cnt);
|
|
`endif
|
|
bus_command <= `BUSCMD_DP_WRITE;
|
|
bus_nibble_in <= (~t_reg)?A[t_offset*4+:4]:C[t_offset*4+:4];
|
|
t_offset <= t_offset + 1;
|
|
t_ctr <= t_ctr + 1;
|
|
if (t_ctr == t_cnt)
|
|
begin
|
|
runstate <= `WRITE_DONE;
|
|
end
|
|
end
|
|
`WRITE_DONE:
|
|
begin
|
|
`ifdef SIM
|
|
$display("`WRITE_DONE | ptr %s | dir %s | reg %s | field %h | off %h | ctr %h | cnt %h",
|
|
t_ptr?"D1":"D0", t_dir?"IN":"OUT", t_reg?"C":"A", t_field, t_offset, t_ctr, t_cnt);
|
|
`endif
|
|
bus_command <= `BUSCMD_NOP;
|
|
runstate <= `NEXT_INSTR;
|
|
decstate <= DECODE_START;
|
|
end
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("runstate %h | decstate %h | ptr %s | dir %s | reg %s | field %h",
|
|
runstate, decstate, t_ptr?"D1":"D0", t_dir?"IN":"OUT", t_reg?"C":"A", t_field);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
|
|
/******************************************************************************
|
|
*1bnnnnn DO=(5) nnnnn
|
|
*
|
|
*
|
|
*/
|
|
|
|
//if (decstate == DECODE_D0_EQ_5N)
|
|
DECODE_D0_EQ_5N:
|
|
case (runstate)
|
|
`RUN_DECODE:
|
|
begin
|
|
runstate <= `INSTR_START;
|
|
t_cnt <= 4;
|
|
t_ctr <= 0;
|
|
`ifdef SIM
|
|
$write("%5h D0=(5)\t", saved_PC);
|
|
`endif
|
|
end
|
|
`INSTR_START, `INSTR_STROBE: begin end
|
|
`INSTR_READY:
|
|
begin
|
|
D0[t_ctr*4+:4] <= nibble;
|
|
`ifdef SIM
|
|
$write("%1h", nibble);
|
|
`endif
|
|
if (t_ctr == t_cnt)
|
|
begin
|
|
`ifdef SIM
|
|
$display("");
|
|
`endif
|
|
runstate <= `NEXT_INSTR;
|
|
decstate <= DECODE_START;
|
|
end
|
|
else
|
|
begin
|
|
t_ctr <= t_ctr + 1;
|
|
runstate <= `INSTR_START;
|
|
end
|
|
end
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("DECODE_D0_EQ_5N: runstate %h", runstate);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
|
|
/******************************************************************************
|
|
* 2n P= n
|
|
*
|
|
*
|
|
*/
|
|
|
|
//if (decstate == DECODE_P_EQ)
|
|
DECODE_P_EQ:
|
|
case (runstate)
|
|
`RUN_DECODE: runstate <= `INSTR_START;
|
|
`INSTR_START, `INSTR_STROBE: begin end
|
|
`INSTR_READY:
|
|
begin
|
|
P <= nibble;
|
|
`ifdef SIM
|
|
$display("%05h P=\t%h", saved_PC, nibble);
|
|
`endif
|
|
runstate <= `NEXT_INSTR;
|
|
decstate <= DECODE_START;
|
|
end
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("DECODE_P_EQ: runstate %h", runstate);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
|
|
|
|
/******************************************************************************
|
|
* 3n[xxxxxx] LC (n) [xxxxxx]
|
|
*
|
|
*
|
|
*/
|
|
|
|
//if ((decstate == DECODE_LC_LEN) | (decstate == DECODE_LC))
|
|
DECODE_LC_LEN, DECODE_LC:
|
|
case (runstate)
|
|
`RUN_DECODE: runstate <= `INSTR_START;
|
|
`INSTR_START, `INSTR_STROBE: begin end
|
|
`INSTR_READY:
|
|
case (decstate)
|
|
DECODE_LC_LEN:
|
|
begin
|
|
`ifdef SIM
|
|
$write("%5h LC (%h)\t", saved_PC, nibble);
|
|
`endif
|
|
t_cnt <= nibble;
|
|
t_ctr <= 0;
|
|
decstate <= DECODE_LC;
|
|
runstate <= `INSTR_START;
|
|
end
|
|
DECODE_LC:
|
|
begin
|
|
C[((t_ctr+P)%16)*4+:4] <= nibble;
|
|
`ifdef SIM
|
|
$write("%1h", nibble);
|
|
`endif
|
|
if (t_ctr == t_cnt)
|
|
begin
|
|
`ifdef SIM
|
|
$display("");
|
|
`endif
|
|
runstate <= `NEXT_INSTR;
|
|
decstate <= DECODE_START;
|
|
|
|
end
|
|
else
|
|
begin
|
|
t_ctr <= (t_ctr + 1)&4'hf;
|
|
runstate <= `INSTR_START;
|
|
end
|
|
end
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("decstate %h nibble %h", decstate, nibble);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("DECODE_LC decstate %h", decstate);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
|
|
/******************************************************************************
|
|
* 6zyx GOTO xyz
|
|
*
|
|
*
|
|
*/
|
|
|
|
//if (decstate == DECODE_GOTO)
|
|
DECODE_GOTO:
|
|
case (runstate)
|
|
`RUN_DECODE:
|
|
begin
|
|
runstate <= `INSTR_START;
|
|
jump_base <= PC;
|
|
jump_offset <= 0;
|
|
t_cnt <= 2;
|
|
t_ctr <= 0;
|
|
end
|
|
`INSTR_START, `INSTR_STROBE: begin end
|
|
`INSTR_READY:
|
|
begin
|
|
jump_offset[t_ctr*4+:4] <= nibble;
|
|
if (t_ctr == t_cnt)
|
|
begin
|
|
// load PC
|
|
runstate <= `RUN_EXEC;
|
|
end
|
|
else
|
|
begin
|
|
t_ctr <= t_ctr + 1;
|
|
runstate <= `INSTR_START;
|
|
end
|
|
end
|
|
`RUN_EXEC:
|
|
begin
|
|
`ifdef SIM
|
|
$display("%5h GOTO\t%3h\t=> %05h", saved_PC, jump_offset[11:0], jump_base + jump_offset);
|
|
`endif
|
|
PC <= jump_base + jump_offset;
|
|
bus_load_pc <= 1;
|
|
runstate <= `NEXT_INSTR;
|
|
decstate <= DECODE_START;
|
|
end
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("DECODE_GOTO: runstate %h", runstate);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
|
|
/******************************************************************************
|
|
* 8
|
|
* a lot of things start with 8...
|
|
*
|
|
*/
|
|
|
|
//if (decstate == DECODE_8)
|
|
DECODE_8:
|
|
case (runstate)
|
|
`RUN_DECODE: runstate <= `INSTR_START;
|
|
`INSTR_START, `INSTR_STROBE: begin end
|
|
`INSTR_READY:
|
|
begin
|
|
case (nibble)
|
|
4'h0: decstate <= DECODE_80;
|
|
4'h2: decstate <= DECODE_82;
|
|
4'h4: decstate <= DECODE_ST_EQ_0_N;
|
|
4'h5: decstate <= DECODE_ST_EQ_1_N;
|
|
4'hd: decstate <= DECODE_GOVLNG;
|
|
4'hf: decstate <= DECODE_GOSBVL;
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("unhandled instruction prefix 8%h", nibble);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
runstate <= `RUN_DECODE;
|
|
end
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("DECODE_8: runstate %h", runstate);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
|
|
/******************************************************************************
|
|
* 80
|
|
* a lot of things start with 80...
|
|
*
|
|
*/
|
|
|
|
//if (decstate == DECODE_80)
|
|
DECODE_80:
|
|
case (runstate)
|
|
`RUN_DECODE: runstate <= `INSTR_START;
|
|
`INSTR_START, `INSTR_STROBE: begin end
|
|
`INSTR_READY:
|
|
begin
|
|
case (nibble)
|
|
4'h5: decstate <= DECODE_CONFIG;
|
|
4'ha: decstate <= DECODE_RESET;
|
|
4'hc: decstate <= DECODE_C_EQ_P_N;
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("unhandled instruction prefix 80%h", nibble);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
runstate <= `RUN_DECODE;
|
|
end
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("DECODE_80: runstate %h", runstate);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
|
|
/*
|
|
|
|
/******************************************************************************
|
|
* 805 CONFIG
|
|
*
|
|
*
|
|
*/
|
|
|
|
//if ((decstate == DECODE_CONFIG) & (runstate == `RUN_DECODE))
|
|
DECODE_CONFIG:
|
|
if (runstate == `RUN_DECODE)
|
|
begin
|
|
`ifdef SIM
|
|
$display("%05h CONFIG\t\t\t<= NOT IMPLEMENTED YET", saved_PC);
|
|
`endif
|
|
runstate <= `NEXT_INSTR;
|
|
decstate <= DECODE_START;
|
|
end
|
|
|
|
/******************************************************************************
|
|
* 80A RESET
|
|
*
|
|
*
|
|
*/
|
|
|
|
//if ((decstate == DECODE_RESET) & (runstate == `RUN_DECODE))
|
|
DECODE_RESET:
|
|
if (runstate == `RUN_DECODE)
|
|
begin
|
|
`ifdef SIM
|
|
$display("%05h RESET\t\t\t<= NOT IMPLEMENTED YET", saved_PC);
|
|
`endif
|
|
runstate <= `NEXT_INSTR;
|
|
decstate <= DECODE_START;
|
|
end
|
|
|
|
/******************************************************************************
|
|
* 80Cn C=P n
|
|
*
|
|
*
|
|
*/
|
|
|
|
//if (decstate == DECODE_C_EQ_P_N)
|
|
DECODE_C_EQ_P_N:
|
|
case (runstate)
|
|
`RUN_DECODE: runstate <= `INSTR_START;
|
|
`INSTR_START, `INSTR_STROBE: begin end
|
|
`INSTR_READY:
|
|
begin
|
|
C[nibble*4+:4] <= P;
|
|
`ifdef SIM
|
|
$display("%05h C=P\t%h", saved_PC, nibble);
|
|
`endif
|
|
runstate <= `NEXT_INSTR;
|
|
decstate <= DECODE_START;
|
|
end
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("DECODE_80C runstate %h", runstate);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
|
|
|
|
/******************************************************************************
|
|
* 82x
|
|
*
|
|
* lots of things there
|
|
*
|
|
*/
|
|
|
|
//if (decstate == DECODE_82)
|
|
DECODE_82:
|
|
case (runstate)
|
|
`RUN_DECODE: runstate <= `INSTR_START;
|
|
`INSTR_START, `INSTR_STROBE: begin end
|
|
`INSTR_READY:
|
|
begin
|
|
HST <= HST & ~nibble;
|
|
`ifdef SIM
|
|
case (nibble)
|
|
4'h1: $display("%5h XM=0", saved_PC);
|
|
4'h2: $display("%5h SB=0", saved_PC);
|
|
4'h4: $display("%5h SR=0", saved_PC);
|
|
4'h8: $display("%5h MP=0", saved_PC);
|
|
4'hf: $display("%5h CLRHST", saved_PC);
|
|
default: $display("%5h CLRHST %f", saved_PC, nibble);
|
|
endcase
|
|
`endif
|
|
runstate <= `NEXT_INSTR;
|
|
decstate <= DECODE_START;
|
|
end
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("DECODE_82 runstate %h", runstate);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
|
|
/******************************************************************************
|
|
* 84n ST=0 n
|
|
* 85n ST=1 n
|
|
*/
|
|
|
|
//if ((decstate == DECODE_ST_EQ_0_N) | (decstate == DECODE_ST_EQ_1_N))
|
|
DECODE_ST_EQ_0_N, DECODE_ST_EQ_1_N:
|
|
case (runstate)
|
|
`RUN_DECODE: runstate <= `INSTR_START;
|
|
`INSTR_START, `INSTR_STROBE: begin end
|
|
`INSTR_READY:
|
|
begin
|
|
case (decstate)
|
|
DECODE_ST_EQ_0_N:
|
|
begin
|
|
`ifdef SIM
|
|
$display("%05h ST=0\t%h", saved_PC, nibble);
|
|
`endif
|
|
ST[nibble] <= 0;
|
|
end
|
|
DECODE_ST_EQ_1_N:
|
|
begin
|
|
`ifdef SIM
|
|
$display("%05h ST=1\t%h", saved_PC, nibble);
|
|
`endif
|
|
ST[nibble] <= 1;
|
|
end
|
|
endcase
|
|
runstate <= `NEXT_INSTR;
|
|
decstate <= DECODE_START;
|
|
end
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("decstate %h", decstate);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
|
|
/******************************************************************************
|
|
* 8Dzyxwv GOVLNG vwxyz
|
|
* 8Fzyxwv GOSBVL vwxyz
|
|
* two for the price of one...
|
|
*/
|
|
|
|
//if ((decstate == DECODE_GOVLNG) | (decstate == DECODE_GOSBVL))
|
|
DECODE_GOVLNG, DECODE_GOSBVL:
|
|
case (runstate)
|
|
`RUN_DECODE:
|
|
begin
|
|
jump_base <= 0;
|
|
t_cnt <= 4;
|
|
t_ctr <= 0;
|
|
if (decstate == DECODE_GOSBVL)
|
|
rstk_ptr <= rstk_ptr + 1;
|
|
runstate <= `INSTR_START;
|
|
end
|
|
`INSTR_START, `INSTR_STROBE: begin end
|
|
`INSTR_READY:
|
|
begin
|
|
//$display("decstate %h | nibble %h", decstate, nibble);
|
|
jump_base[t_ctr*4+:4] <= nibble;
|
|
if (t_ctr == t_cnt) runstate <= `RUN_EXEC;
|
|
else
|
|
begin
|
|
t_ctr <= t_ctr + 1;
|
|
runstate <= `INSTR_START;
|
|
end
|
|
end
|
|
`RUN_EXEC:
|
|
begin
|
|
`ifdef SIM
|
|
$write("%5h GO", saved_PC);
|
|
case (decstate)
|
|
DECODE_GOVLNG: $write("VLNG");
|
|
DECODE_GOSBVL: $write("SBVL");
|
|
endcase
|
|
$display("\t%5h", jump_base);
|
|
`endif
|
|
if (decstate == DECODE_GOSBVL)
|
|
RSTK[rstk_ptr] <= PC;
|
|
PC <= jump_base;
|
|
bus_load_pc <= 1;
|
|
runstate <= `NEXT_INSTR;
|
|
decstate <= DECODE_START;
|
|
end
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("decstate %h", decstate);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
|
|
/******************************************************************************
|
|
* A[ab]x
|
|
*
|
|
* lots of things there
|
|
*
|
|
*/
|
|
|
|
//if ((decstate == DECODE_A)|(decstate == DECODE_A_FS))
|
|
DECODE_A, DECODE_A_FS:
|
|
case (runstate)
|
|
`RUN_DECODE: runstate <= `INSTR_START;
|
|
`INSTR_START, `INSTR_STROBE: begin end
|
|
`INSTR_READY:
|
|
case (decstate)
|
|
DECODE_A:
|
|
begin
|
|
t_field <= nibble;
|
|
decstate <= DECODE_A_FS;
|
|
runstate <= `INSTR_START;
|
|
end
|
|
DECODE_A_FS:
|
|
begin
|
|
case (nibble)
|
|
4'h2:
|
|
case (t_field)
|
|
4'he:
|
|
begin
|
|
C[7:0] <= 0;
|
|
`ifdef SIM
|
|
$display("%5h C=0\tB", saved_PC);
|
|
`endif
|
|
end
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("decstate %h %h %h", decstate, t_field, nibble);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("decstate %h %h %h", decstate, t_field, nibble);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
runstate <= `NEXT_INSTR;
|
|
decstate <= DECODE_START;
|
|
end
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("decstate %h %h", decstate, nibble);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("decstate %h", decstate);
|
|
`endif
|
|
halt <= 1;
|
|
end
|
|
endcase
|
|
|
|
|
|
default:
|
|
begin
|
|
`ifdef SIM
|
|
$display("decstate %h not handled", decstate);
|
|
`endif
|
|
//halt <= 1;
|
|
end
|
|
endcase
|
|
|
|
/**************************************************************************************************
|
|
*
|
|
* Dump all registers to leds, one piece at a time
|
|
*
|
|
*/
|
|
|
|
`ifndef SIM
|
|
|
|
case (regdump)
|
|
REGDMP_HEX: led <= {7'b0000000, hex_dec};
|
|
default: led <= 8'b11111111;
|
|
endcase
|
|
regdump <= regdump + 1;
|
|
|
|
`endif
|
|
|
|
end
|
|
// Verilator lint_off UNUSED
|
|
//wire [N-1:0] unused;
|
|
//assign unused = { };
|
|
// Verilator lint_on UNUSED
|
|
endmodule
|
|
|
|
`ifdef SIM
|
|
|
|
module saturn_tb;
|
|
reg clk;
|
|
reg reset;
|
|
wire halt;
|
|
wire [3:0] runstate;
|
|
wire [4:0] decstate;
|
|
|
|
saturn_core saturn (
|
|
.clk (clk),
|
|
.reset (reset),
|
|
.halt (halt),
|
|
.runstate (runstate),
|
|
.decstate (decstate)
|
|
);
|
|
|
|
always
|
|
#10 clk = (clk === 1'b0);
|
|
|
|
initial begin
|
|
//$monitor ("c %b | r %b | run %h | dec %h", clk, reset, runstate, decstate);
|
|
end
|
|
|
|
initial begin
|
|
$display("starting the simulation");
|
|
clk <= 0;
|
|
reset <= 1;
|
|
@(posedge clk);
|
|
reset <= 0;
|
|
@(posedge halt);
|
|
$finish;
|
|
end
|
|
|
|
|
|
endmodule
|
|
|
|
`else
|
|
|
|
|
|
`endif |