2011-11-01 20:56:30 +01:00

883 lines
26 KiB
Verilog

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer:
//
// Create Date: 23:14:37 10/13/2011
// Design Name:
// Module Name: cx4
// Project Name:
// Target Devices:
// Tool versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module cx4(
input [7:0] DI,
output [7:0] DO,
input [12:0] ADDR,
input CS,
input SNES_VECT_EN,
input nRD,
input nWR,
input CLK,
input [23:0] DATROM_DI,
input DATROM_WE,
input [9:0] DATROM_ADDR,
input [7:0] BUS_DI,
output [23:0] BUS_ADDR,
output BUS_RRQ,
input BUS_RDY,
output cx4_active
);
reg [2:0] cx4_busy;
parameter BUSY_CACHE = 2'b00;
parameter BUSY_DMA = 2'b01;
parameter BUSY_CPU = 2'b10;
wire datram_enable = CS & (ADDR[11:0] < 12'hc00);
wire mmio_enable = CS & (ADDR[12:5] == 8'b11111010) & (ADDR[4:0] < 5'b10011);
wire status_enable = CS & (ADDR[12:5] == 8'b11111010) & (ADDR[4:0] >= 5'b10011);
wire vector_enable = (CS & (ADDR[12:5] == 8'b11111011)) | (cx4_active & SNES_VECT_EN);
wire gpr_enable = CS & (&(ADDR[12:7]) && ADDR[5:4] != 2'b11);
wire pgmrom_enable = CS & (ADDR[12:5] == 8'b11110000);
wire [7:0] DATRAM_DO;
reg [7:0] MMIO_DOr;
wire [7:0] MMIO_DO;
wire [7:0] STATUS_DO;
wire [7:0] VECTOR_DO;
wire [7:0] GPR_DO;
assign DO = datram_enable ? DATRAM_DO
: mmio_enable ? MMIO_DO
: status_enable ? STATUS_DO
: vector_enable ? VECTOR_DO
: gpr_enable ? GPR_DO
: 8'h00;
/* 0x1f40 - 0x1f52: MMIO
SNES: 8 bits / CX4: various */
reg [23:0] cx4_mmio_dmasrc;
reg [15:0] cx4_mmio_dmalen;
reg [23:0] cx4_mmio_dmatgt;
reg cx4_mmio_cachepage;
reg [23:0] cx4_mmio_pgmoff;
reg [1:0] cx4_mmio_savepage;
reg [14:0] cx4_mmio_pgmpage;
reg [7:0] cx4_mmio_pc;
reg [7:0] cx4_mmio_r1f50;
reg cx4_mmio_r1f51;
reg cx4_mmio_r1f52;
/* 0x1f53 - 0x1f5f: status register */
assign cx4_active = |cx4_busy;
/* 0x1f60 - 0x1f7f: reset vectors */
reg [7:0] vector [31:0];
/* 0x1f80 - 0x1faf (0x1fc0 - 0x1fef): general purpose register file
SNES: 8 bits / CX4: 24 bits */
reg [7:0] gpr [47:0];
wire [47:0] cpu_mul_result;
reg [14:0] cx4_mmio_pagemem[1:0];
reg [23:0] const [15:0];
reg [14:0] cachetag [1:0];
reg [1:0] cachevalid;
reg [14:0] cache_pgmpage;
reg [14:0] cpu_cache_pgmpage;
reg cache_cachepage;
reg cpu_cache_cachepage;
reg cpu_cache_done;
reg [7:0] cpu_pc_stack [7:0];
reg [7:0] cpu_page_stack;
initial begin
cache_pgmpage = 15'b0;
cpu_cache_pgmpage = 15'b0;
cache_cachepage = 1'b0;
cpu_cache_cachepage = 1'b0;
cpu_cache_done = 1'b0;
cachetag[0] = 14'h0000;
cachetag[1] = 14'h0000;
cachevalid = 2'b00;
cx4_busy = 3'b000;
cx4_mmio_pgmoff = 24'h000000;
cx4_mmio_pgmpage = 15'h0000;
cx4_mmio_dmasrc = 24'h000000;
cx4_mmio_dmalen = 16'h0000;
cx4_mmio_dmatgt = 24'h000000;
cx4_mmio_savepage = 2'b00;
const[0] = 24'h000000;
const[1] = 24'hffffff;
const[2] = 24'h00ff00;
const[3] = 24'hff0000;
const[4] = 24'h00ffff;
const[5] = 24'hffff00;
const[6] = 24'h800000;
const[7] = 24'h7fffff;
const[8] = 24'h008000;
const[9] = 24'h007fff;
const[10] = 24'hff7fff;
const[11] = 24'hffff7f;
const[12] = 24'h010000;
const[13] = 24'hfeffff;
const[14] = 24'h000100;
const[15] = 24'h00feff;
cpu_pc_stack[0] = 8'b0;
cpu_pc_stack[1] = 8'b0;
cpu_pc_stack[2] = 8'b0;
cpu_pc_stack[3] = 8'b0;
cpu_pc_stack[4] = 8'b0;
cpu_pc_stack[5] = 8'b0;
cpu_pc_stack[6] = 8'b0;
cpu_pc_stack[7] = 8'b0;
cpu_page_stack = 8'b0;
end
assign MMIO_DO = MMIO_DOr;
assign VECTOR_DO = vector [ADDR[4:0]];
assign GPR_DO = gpr [ADDR[5:0]];
assign STATUS_DO = {1'b0, cx4_active, 4'b0000, ~cx4_active, 1'b0};
reg [7:0] DIr;
always @(posedge CLK) DIr <= DI;
reg [4:0] datram_enable_sreg;
initial datram_enable_sreg = 5'b11111;
always @(posedge CLK) datram_enable_sreg <= {datram_enable_sreg[3:0], datram_enable};
reg [5:0] nWR_sreg;
always @(posedge CLK) nWR_sreg <= {nWR_sreg[4:0], nWR};
wire WR_EN = (nWR_sreg[5:0] == 6'b000001);
wire DATRAM_WR_EN = datram_enable & WR_EN;
wire MMIO_WR_EN = mmio_enable & WR_EN;
wire VECTOR_WR_EN = vector_enable & WR_EN;
wire GPR_WR_EN = gpr_enable & WR_EN;
reg [23:0] cpu_idb; // tmp register for reg file read
/* Need to cache when:
1f48 is written
AND (selected cache page is invalid
OR selected cache page does not contain requested page already)
*/
reg CACHE_TRIG_ENr;
reg CACHE_TRIG_EN2r;
reg cpu_cache_en;
initial begin
CACHE_TRIG_ENr = 1'b0;
CACHE_TRIG_EN2r = 1'b0;
cpu_cache_en = 1'b0;
end
always @(posedge CLK) CACHE_TRIG_EN2r <= CACHE_TRIG_ENr;
wire CACHE_TRIG_EN = CACHE_TRIG_EN2r;
reg DMA_TRIG_ENr;
initial DMA_TRIG_ENr = 1'b0;
wire DMA_TRIG_EN = DMA_TRIG_ENr;
reg CACHE_BUS_RRQr;
reg DMA_BUS_RRQr;
reg cpu_bus_rq;
initial begin
CACHE_BUS_RRQr = 1'b0;
DMA_BUS_RRQr = 1'b0;
cpu_bus_rq = 1'b0;
end
assign BUS_RRQ = CACHE_BUS_RRQr | DMA_BUS_RRQr | cpu_bus_rq;
reg cpu_page;
reg [14:0] cpu_p;
reg [7:0] cpu_pc;
reg [23:0] cpu_a;
reg fl_n;
reg fl_z;
reg fl_c;
reg cpu_go_en_r;
initial cpu_go_en_r = 1'b0;
initial begin
cx4_mmio_r1f50 = 8'h33;
cx4_mmio_r1f51 = 1'b0;
cx4_mmio_r1f52 = 1'b1;
end
always @(posedge CLK) begin
case (ADDR[4:0])
5'h00: MMIO_DOr <= cx4_mmio_dmasrc[7:0]; // 1f40
5'h01: MMIO_DOr <= cx4_mmio_dmasrc[15:8]; // 1f41
5'h02: MMIO_DOr <= cx4_mmio_dmasrc[23:16]; // 1f42
5'h03: MMIO_DOr <= cx4_mmio_dmalen[7:0]; // 1f43
5'h04: MMIO_DOr <= cx4_mmio_dmalen[15:8]; // 1f44
5'h05: MMIO_DOr <= cx4_mmio_dmatgt[7:0]; // 1f45
5'h06: MMIO_DOr <= cx4_mmio_dmatgt[15:8]; // 1f46
5'h07: MMIO_DOr <= cx4_mmio_dmatgt[23:16]; // 1f47
5'h08: MMIO_DOr <= {7'b0, cx4_mmio_cachepage};
5'h09: MMIO_DOr <= cx4_mmio_pgmoff[7:0]; // 1f49
5'h0a: MMIO_DOr <= cx4_mmio_pgmoff[15:8]; // 1f4a
5'h0b: MMIO_DOr <= cx4_mmio_pgmoff[23:16]; // 1f4b
5'h0c: MMIO_DOr <= {6'b0, cx4_mmio_savepage}; // 1f4c
5'h0d: MMIO_DOr <= cx4_mmio_pgmpage[7:0]; // 1f4d
5'h0e: MMIO_DOr <= {1'b0, cx4_mmio_pgmpage[14:8]}; // 1f4e
5'h0f: MMIO_DOr <= cx4_mmio_pc; // 1f4f
5'h10: MMIO_DOr <= cx4_mmio_r1f50; // 1f50
5'h11: MMIO_DOr <= {7'b0, cx4_mmio_r1f51}; // 1f51
5'h12: MMIO_DOr <= {7'b0, cx4_mmio_r1f52}; // 1f52
default: MMIO_DOr <= 8'hff;
endcase
end
always @(posedge CLK) begin
if(MMIO_WR_EN) begin
case(ADDR[4:0])
5'h00: cx4_mmio_dmasrc[7:0] <= DIr; // 1f40
5'h01: cx4_mmio_dmasrc[15:8] <= DIr; // 1f41
5'h02: cx4_mmio_dmasrc[23:16] <= DIr; // 1f42
5'h03: cx4_mmio_dmalen[7:0] <= DIr; // 1f43
5'h04: cx4_mmio_dmalen[15:8] <= DIr; // 1f44
5'h05: cx4_mmio_dmatgt[7:0] <= DIr; // 1f45
5'h06: cx4_mmio_dmatgt[15:8] <= DIr; // 1f46
5'h07: begin
cx4_mmio_dmatgt[23:16] <= DIr; // 1f47
DMA_TRIG_ENr <= 1'b1;
end
5'h08: begin
cx4_mmio_cachepage <= DIr[0]; // 1f48
CACHE_TRIG_ENr <= 1'b1;
end
5'h09: cx4_mmio_pgmoff[7:0] <= DIr; // 1f49
5'h0a: cx4_mmio_pgmoff[15:8] <= DIr; // 1f4a
5'h0b: cx4_mmio_pgmoff[23:16] <= DIr; // 1f4b
5'h0c: begin
cx4_mmio_savepage <= DIr[1:0];
if(DIr[0]) cx4_mmio_pagemem[0] <= cx4_mmio_pgmpage;
if(DIr[1]) cx4_mmio_pagemem[1] <= cx4_mmio_pgmpage;
end
5'h0d: cx4_mmio_pgmpage[7:0] <= DIr; // 1f4d
5'h0e: cx4_mmio_pgmpage[14:8] <= DIr[6:0]; // 1f4e
5'h0f: begin
cx4_mmio_pc <= DIr; // 1f4f
if(cx4_mmio_savepage[0]
&& cx4_mmio_pagemem[0] == cx4_mmio_pgmpage)
cx4_mmio_cachepage <= 1'b0;
else if(cx4_mmio_savepage[1]
&& cx4_mmio_pagemem[1] == cx4_mmio_pgmpage)
cx4_mmio_cachepage <= 1'b1;
cpu_go_en_r <= 1'b1;
end
5'h10: cx4_mmio_r1f50 <= DIr & 8'h77; // 1f50
5'h11: cx4_mmio_r1f51 <= DIr[0]; // 1f51
5'h12: cx4_mmio_r1f52 <= DIr[0]; // 1f52
endcase
end else begin
CACHE_TRIG_ENr <= 1'b0;
DMA_TRIG_ENr <= 1'b0;
cpu_go_en_r <= 1'b0;
end
end
always @(posedge CLK) begin
if(VECTOR_WR_EN) vector[ADDR[4:0]] <= DIr;
end
reg [4:0] CACHE_ST;
parameter ST_CACHE_IDLE = 5'b00001;
parameter ST_CACHE_START = 5'b00010;
parameter ST_CACHE_WAIT = 5'b00100;
parameter ST_CACHE_ADDR = 5'b01000;
parameter ST_CACHE_END = 5'b10000;
initial CACHE_ST = ST_CACHE_IDLE;
reg [4:0] DMA_ST;
parameter ST_DMA_IDLE = 5'b00001;
parameter ST_DMA_START = 5'b00010;
parameter ST_DMA_WAIT = 5'b00100;
parameter ST_DMA_ADDR = 5'b01000;
parameter ST_DMA_END = 5'b10000;
initial DMA_ST = ST_DMA_IDLE;
reg [23:0] CACHE_SRC_ADDRr;
wire [22:0] MAPPED_CACHE_SRC_ADDR = {CACHE_SRC_ADDRr[23:16],CACHE_SRC_ADDRr[14:0]};
reg [23:0] DMA_SRC_ADDRr;
wire [22:0] MAPPED_DMA_SRC_ADDR = {DMA_SRC_ADDRr[23:16],DMA_SRC_ADDRr[14:0]};
wire [22:0] MAPPED_CPU_BUS_ADDR;
assign BUS_ADDR = cx4_busy[BUSY_CACHE] ? MAPPED_CACHE_SRC_ADDR
: cx4_busy[BUSY_DMA] ? MAPPED_DMA_SRC_ADDR
: MAPPED_CPU_BUS_ADDR;
reg cx4_pgmrom_we;
initial cx4_pgmrom_we = 1'b0;
reg [9:0] cx4_pgmrom_addr;
reg [19:0] cache_count;
initial cache_count = 20'b0;
always @(posedge CLK) begin
case(CACHE_ST)
ST_CACHE_IDLE: begin
if(CACHE_TRIG_EN
& (~cachevalid[cx4_mmio_cachepage]
| |(cachetag[cx4_mmio_cachepage] ^ cx4_mmio_pgmpage))) begin
CACHE_ST <= ST_CACHE_START;
cache_pgmpage <= cx4_mmio_pgmpage;
cache_cachepage <= cx4_mmio_cachepage;
end else if(cpu_cache_en
& (~cachevalid[~cpu_page]
| |(cachetag[~cpu_page] ^ cpu_p))) begin
CACHE_ST <= ST_CACHE_START;
cache_pgmpage <= cpu_p;
cache_cachepage <= ~cpu_page;
cx4_busy[BUSY_CACHE] <= 1'b1;
end
else CACHE_ST <= ST_CACHE_IDLE;
end
ST_CACHE_START: begin
cx4_busy[BUSY_CACHE] <= 1'b1;
CACHE_SRC_ADDRr <= cx4_mmio_pgmoff + {cache_pgmpage, 9'b0};
cx4_pgmrom_addr <= {cache_cachepage, 9'b0};
CACHE_ST <= ST_CACHE_WAIT;
cache_count <= 10'b0;
CACHE_BUS_RRQr <= 1'b1;
end
ST_CACHE_WAIT: begin
CACHE_BUS_RRQr <= 1'b0;
if(~CACHE_BUS_RRQr & BUS_RDY) begin
CACHE_ST <= ST_CACHE_ADDR;
cx4_pgmrom_we <= 1'b1;
cache_count <= cache_count + 1;
end else CACHE_ST <= ST_CACHE_WAIT;
end
ST_CACHE_ADDR: begin
cx4_pgmrom_we <= 1'b0;
CACHE_SRC_ADDRr <= CACHE_SRC_ADDRr + 1;
cx4_pgmrom_addr <= cx4_pgmrom_addr + 1;
if(cache_count == 9'h1ff) begin
cx4_busy[BUSY_CACHE] <= 1'b0;
cachetag[cache_cachepage] <= cache_pgmpage;
cachevalid[cache_cachepage] <= 1'b1;
CACHE_ST <= ST_CACHE_IDLE;
end else begin
CACHE_BUS_RRQr <= 1'b1;
CACHE_ST <= ST_CACHE_WAIT;
end
end
endcase
end
reg cx4_dma_datram_we;
reg cx4_cpu_datram_we;
initial cx4_dma_datram_we = 1'b0;
initial cx4_cpu_datram_we = 1'b0;
wire cx4_datram_we = cx4_dma_datram_we | cx4_cpu_datram_we;
reg [11:0] cx4_dma_datram_addr;
reg [11:0] cx4_cpu_datram_addr;
wire [11:0] cx4_datram_addr = cx4_busy[BUSY_DMA] ? cx4_dma_datram_addr : cx4_cpu_datram_addr;
reg [23:0] cx4_cpu_datram_di;
wire [7:0] cx4_datram_di = cx4_busy[BUSY_DMA] ? BUS_DI : cx4_cpu_datram_di;
reg [15:0] dma_count;
initial dma_count = 16'b0;
always @(posedge CLK) begin
case(DMA_ST)
ST_DMA_IDLE: begin
if(DMA_TRIG_EN) begin
DMA_ST <= ST_DMA_START;
end else DMA_ST <= ST_DMA_IDLE;
end
ST_DMA_START: begin
cx4_busy[BUSY_DMA] <= 1'b1;
DMA_SRC_ADDRr <= cx4_mmio_dmasrc;
cx4_dma_datram_addr <= (cx4_mmio_dmatgt & 24'h000fff);
DMA_ST <= ST_DMA_WAIT;
dma_count <= cx4_mmio_dmalen;
DMA_BUS_RRQr <= 1'b1;
end
ST_DMA_WAIT: begin
DMA_BUS_RRQr <= 1'b0;
if(~DMA_BUS_RRQr & BUS_RDY) begin
DMA_ST <= ST_DMA_ADDR;
cx4_dma_datram_we <= 1'b1;
dma_count <= dma_count - 1;
end else DMA_ST <= ST_DMA_WAIT;
end
ST_DMA_ADDR: begin
cx4_dma_datram_we <= 1'b0;
DMA_SRC_ADDRr <= DMA_SRC_ADDRr + 1;
cx4_dma_datram_addr <= cx4_dma_datram_addr + 1;
if(dma_count == 16'h0000) begin
cx4_busy[BUSY_DMA] <= 1'b0;
DMA_ST <= ST_DMA_IDLE;
end else begin
DMA_BUS_RRQr <= 1'b1;
DMA_ST <= ST_DMA_WAIT;
end
end
endcase
end
/***************************
=========== CPU ===========
***************************/
reg [4:0] CPU_STATE;
reg [2:0] cpu_sp;
initial cpu_sp = 3'b000;
wire [15:0] cpu_op_w;
reg [15:0] cpu_op;
reg [23:0] cpu_busdata;
reg [23:0] cpu_romdata;
reg [23:0] cpu_ramdata;
reg [23:0] cpu_busaddr;
assign MAPPED_CPU_BUS_ADDR = {cpu_busaddr[23:16], cpu_busaddr[14:0]};
reg [23:0] cpu_romaddr;
reg [23:0] cpu_ramaddr;
reg [23:0] cpu_acch;
reg [23:0] cpu_accl;
reg [23:0] cpu_mul_src;
reg [24:0] cpu_alu_res;
reg [23:0] cpu_dummy;
reg [23:0] cpu_tmp;
reg [23:0] cpu_sa; // tmp register for shifted accumulator
wire [9:0] cx4_datrom_addr = cpu_a[9:0];
wire [23:0] cx4_datrom_do;
wire [7:0] cx4_datram_do;
parameter ST_CPU_IDLE = 5'b00001;
parameter ST_CPU_0 = 5'b00010;
parameter ST_CPU_1 = 5'b00100;
parameter ST_CPU_2 = 5'b01000;
parameter ST_CPU_3 = 5'b10000;
initial CPU_STATE = ST_CPU_IDLE;
parameter OP_NOP = 5'b00000;
parameter OP_JP = 5'b00001;
parameter OP_SKIP = 5'b00010;
parameter OP_RT = 5'b00011;
parameter OP_LD = 5'b00100;
parameter OP_ST = 5'b00101;
parameter OP_SWP = 5'b00110;
parameter OP_RDROM = 5'b00111;
parameter OP_RDRAM = 5'b01000;
parameter OP_WRRAM = 5'b01001;
parameter OP_ALU = 5'b01010;
parameter OP_MUL = 5'b01011;
parameter OP_WAI = 5'b01100;
parameter OP_BUS = 5'b01101;
parameter OP_CMP = 5'b01110;
parameter OP_SEX = 5'b01111;
parameter OP_HLT = 5'b10000;
wire [6:0] op_id = cpu_op_w[15:10];
reg [7:0] op_param;
reg [4:0] op;
reg [1:0] op_sa;
reg op_imm;
reg op_p;
reg op_call;
reg op_jump;
reg condtrue;
always @(posedge CLK) begin
if(cpu_go_en_r) cx4_busy[BUSY_CPU] <= 1'b1;
else if(op == OP_HLT) cx4_busy[BUSY_CPU] <= 1'b0;
end
always @(posedge CLK) begin
case(op_sa)
2'b00: cpu_sa <= cpu_a;
2'b01: cpu_sa <= cpu_a << 1;
2'b10: cpu_sa <= cpu_a << 8;
2'b11: cpu_sa <= cpu_a << 16;
endcase
end
reg jp_docache;
initial jp_docache = 1'b0;
always @(posedge CLK) begin
case(CPU_STATE)
ST_CPU_IDLE: begin
if(cpu_go_en_r) begin
cpu_pc <= cx4_mmio_pc;
cpu_page <= cx4_mmio_cachepage;
cpu_p <= cx4_mmio_pgmpage;
op <= OP_NOP;
CPU_STATE <= ST_CPU_2;
end
else CPU_STATE <= ST_CPU_IDLE;
end
ST_CPU_0: begin // Phase 0:
cpu_cache_en <= 1'b0;
if(op == OP_HLT) begin
CPU_STATE <= ST_CPU_IDLE;
end
else CPU_STATE <= ST_CPU_1;
case(op)
OP_JP: begin
case(cpu_op[11:10])
2'b10: condtrue <= 1'b1;
2'b11: condtrue <= fl_z;
2'b00: condtrue <= fl_c;
2'b01: condtrue <= fl_n;
endcase
if(op_p && !jp_docache) begin
jp_docache <= 1'b1;
cpu_cache_en <= 1'b1;
end
end
OP_SKIP: begin
case(cpu_op[9:8])
2'b01: condtrue <= (fl_c == cpu_op[0]);
2'b10: condtrue <= (fl_z == cpu_op[0]);
2'b11: condtrue <= (fl_n == cpu_op[0]);
endcase
end
OP_LD, OP_ALU, OP_MUL, OP_CMP, OP_SEX: begin
if(op_imm) cpu_idb <= {16'b0, op_param};
else casex(op_param)
8'h00: cpu_idb <= cpu_a;
8'h01: cpu_idb <= cpu_acch;
8'h02: cpu_idb <= cpu_accl;
8'h03: cpu_idb <= cpu_busdata;
8'h08: cpu_idb <= cpu_romdata;
8'h0c: cpu_idb <= cpu_ramdata;
8'h13: cpu_idb <= cpu_busaddr;
8'h1c: cpu_idb <= cpu_ramaddr;
8'h5x: cpu_idb <= const[op_param[3:0]];
8'h6x: cpu_idb <= {gpr[op_param[3:0]*3+2],
gpr[op_param[3:0]*3+1],
gpr[op_param[3:0]*3]};
default: cpu_idb <= 24'b0;
endcase
end
OP_ST: begin
cpu_idb <= cpu_a;
end
OP_SWP: begin
cpu_idb <= cpu_a;
casex(op_param)
8'h00: cpu_tmp <= cpu_a;
8'h01: cpu_tmp <= cpu_acch;
8'h02: cpu_tmp <= cpu_accl;
8'h03: cpu_tmp <= cpu_busdata;
8'h08: cpu_tmp <= cpu_romdata;
8'h0c: cpu_tmp <= cpu_ramdata;
8'h13: cpu_tmp <= cpu_busaddr;
8'h1c: cpu_tmp <= cpu_ramaddr;
8'h5x: cpu_tmp <= const[op_param[3:0]];
8'h6x: cpu_tmp <= {gpr[op_param[3:0]*3+2],
gpr[op_param[3:0]*3+1],
gpr[op_param[3:0]*3]};
default: cpu_tmp <= 24'b0;
endcase
end
OP_RDRAM, OP_WRRAM: begin
if(op_imm) cx4_cpu_datram_addr <= {16'b0, op_param} + cpu_ramaddr;
else casex(op_param)
8'h00: cx4_cpu_datram_addr <= cpu_a;
8'h01: cx4_cpu_datram_addr <= cpu_acch;
8'h02: cx4_cpu_datram_addr <= cpu_accl;
8'h03: cx4_cpu_datram_addr <= cpu_busdata;
8'h08: cx4_cpu_datram_addr <= cpu_romdata;
8'h0c: cx4_cpu_datram_addr <= cpu_ramdata;
8'h13: cx4_cpu_datram_addr <= cpu_busaddr;
8'h1c: cx4_cpu_datram_addr <= cpu_ramaddr;
8'h5x: cx4_cpu_datram_addr <= const[op_param[3:0]];
8'h6x: cx4_cpu_datram_addr <= {gpr[op_param[3:0]*3+2],
gpr[op_param[3:0]*3+1],
gpr[op_param[3:0]*3]};
default: cx4_cpu_datram_addr <= 24'b0;
endcase
end
OP_BUS: cpu_bus_rq <= 1'b1;
endcase
end
ST_CPU_1: begin
CPU_STATE <= ST_CPU_2;
condtrue <= 1'b0;
case(op)
OP_JP: begin
cpu_cache_en <= 1'b0;
if(!cpu_cache_en && !cx4_busy[BUSY_CACHE]) begin
jp_docache <= 1'b0;
if(condtrue) begin
if(op_call) begin
cpu_page_stack[cpu_sp] <= cpu_page;
cpu_pc_stack[cpu_sp] <= cpu_pc + 1;
cpu_sp <= cpu_sp + 1;
end
cpu_pc <= op_param;
cpu_page <= cpu_page ^ op_p;
end else cpu_pc <= cpu_pc + 1;
end
end
OP_SKIP: begin
if(condtrue) cpu_pc <= cpu_pc + 2;
else cpu_pc <= cpu_pc + 1;
end
OP_RT: begin
cpu_page <= cpu_page_stack[cpu_sp - 1];
cpu_pc <= cpu_pc_stack[cpu_sp - 1];
cpu_sp <= cpu_sp - 1;
end
OP_WAI: if(BUS_RDY) cpu_pc <= cpu_pc + 1;
OP_BUS: begin
cpu_bus_rq <= 1'b0;
cpu_pc <= cpu_pc + 1;
end
default: cpu_pc <= cpu_pc + 1;
endcase
end
ST_CPU_2: begin
CPU_STATE <= ST_CPU_3;
case(op)
OP_LD: begin
casex(cpu_op[11:8])
4'b0x00: cpu_a <= cpu_idb;
4'b0x11: cpu_p <= cpu_idb;
4'b1100: cpu_p[7:0] <= op_param;
4'b1101: cpu_p[14:8] <= op_param;
endcase
end
OP_ST, OP_SWP: begin
casex(op_param)
8'h01: cpu_acch <= cpu_idb;
8'h02: cpu_accl <= cpu_idb;
8'h08: cpu_romdata <= cpu_idb;
8'h0c: cpu_ramdata <= cpu_idb;
8'h13: cpu_busaddr <= cpu_idb;
8'h1c: cpu_ramaddr <= cpu_idb;
endcase
if(op==OP_SWP) cpu_a <= cpu_tmp;
end
OP_RDROM: cpu_romdata <= cx4_datrom_do;
OP_RDRAM: begin
case(cpu_op[9:8])
2'b00: cpu_ramdata[7:0] <= cx4_datram_do;
2'b01: cpu_ramdata[15:8] <= cx4_datram_do;
2'b10: cpu_ramdata[23:16] <= cx4_datram_do;
endcase
end
OP_WRRAM: begin
case(cpu_op[9:8])
2'b00: cx4_cpu_datram_di <= cpu_ramdata[7:0];
2'b01: cx4_cpu_datram_di <= cpu_ramdata[15:8];
2'b10: cx4_cpu_datram_di <= cpu_ramdata[23:16];
endcase
cx4_cpu_datram_we <= 1'b1;
end
OP_CMP: begin
case(cpu_op[15:11])
5'b01001: cpu_alu_res <= cpu_idb - cpu_sa;
5'b01010: cpu_alu_res <= cpu_sa - cpu_idb;
endcase
end
OP_SEX: begin
case(cpu_op[9:8])
2'b01: cpu_alu_res <= {{16{cpu_idb[7]}}, cpu_idb[7:0]};
2'b10: cpu_alu_res <= {{8{cpu_idb[15]}}, cpu_idb[15:0]};
endcase
end
OP_ALU: begin
case(cpu_op[15:11])
5'b10000: cpu_alu_res <= cpu_sa + cpu_idb;
5'b10001: cpu_alu_res <= cpu_idb - cpu_sa;
5'b10010: cpu_alu_res <= cpu_sa - cpu_idb;
5'b10101: cpu_alu_res <= cpu_sa ^ cpu_idb;
5'b10110: cpu_alu_res <= cpu_sa & cpu_idb;
5'b10111: cpu_alu_res <= cpu_sa | cpu_idb;
5'b11000: cpu_alu_res <= cpu_a >> cpu_idb;
5'b11001: cpu_alu_res <= ($signed(cpu_a)) >>> cpu_idb;
5'b11010: {cpu_dummy, cpu_alu_res[23:0]} <= {cpu_a, cpu_a} >> cpu_idb;
5'b11011: cpu_alu_res <= cpu_a << cpu_idb;
endcase
end
endcase
end
ST_CPU_3: begin
CPU_STATE <= ST_CPU_0;
case(op)
OP_BUS: cpu_busaddr <= cpu_busaddr + 1;
OP_WRRAM: cx4_cpu_datram_we <= 1'b0;
OP_CMP: begin
fl_n <= cpu_alu_res[23];
fl_z <= cpu_alu_res[23:0] == 24'b0;
fl_c <= ~cpu_alu_res[24];
end
OP_SEX: cpu_a <= cpu_alu_res[23:0];
OP_ALU: begin
cpu_a <= cpu_alu_res[23:0];
case(cpu_op[15:11])
5'b10000: begin
fl_n <= cpu_alu_res[23];
fl_z <= cpu_alu_res[23:0] == 24'b0;
fl_c <= cpu_alu_res[24];
end
5'b10001, 5'b10010: begin
fl_n <= cpu_alu_res[23];
fl_z <= cpu_alu_res[23:0] == 24'b0;
fl_c <= ~cpu_alu_res[24];
end
default: begin
fl_n <= cpu_alu_res[23];
fl_z <= cpu_alu_res[23:0] == 24'b0;
end
endcase
end
OP_MUL: begin
cpu_acch <= cpu_mul_result[47:24];
cpu_accl <= cpu_mul_result[23:0];
fl_z <= (cpu_mul_result == 48'b0);
fl_n <= cpu_mul_result[47];
end
endcase
cpu_op <= cpu_op_w;
casex(cpu_op_w[15:11])
5'b00000: op <= OP_NOP;
5'b00x01: op <= OP_JP;
5'b00x10: op <= OP_JP;
5'b00100: op <= OP_SKIP;
5'b00111: op <= OP_RT;
5'b01100: op <= OP_LD;
5'b01111: op <= OP_LD;
5'b11100: op <= OP_ST;
5'b11110: op <= OP_SWP;
5'b01110: op <= OP_RDROM;
5'b01101: op <= OP_RDRAM;
5'b11101: op <= OP_WRRAM;
5'b01001: op <= OP_CMP;
5'b01010: op <= OP_CMP;
5'b01011: op <= OP_SEX;
5'b10000: op <= OP_ALU;
5'b10001: op <= OP_ALU;
5'b10010: op <= OP_ALU;
5'b10101: op <= OP_ALU;
5'b10110: op <= OP_ALU;
5'b10111: op <= OP_ALU;
5'b11000: op <= OP_ALU;
5'b11001: op <= OP_ALU;
5'b11010: op <= OP_ALU;
5'b11011: op <= OP_ALU;
5'b10011: op <= OP_MUL;
5'b00011: op <= OP_WAI;
5'b01000: op <= OP_BUS;
5'b11111: op <= OP_HLT;
endcase
op_imm <= cpu_op_w[10];
op_p <= cpu_op_w[9];
op_call <= cpu_op_w[13];
op_param <= cpu_op_w[7:0];
op_sa <= cpu_op_w[9:8];
end
endcase
end
reg[2:0] BUSRD_STATE;
parameter ST_BUSRD_IDLE = 3'b001;
parameter ST_BUSRD_WAIT = 3'b010;
parameter ST_BUSRD_END = 3'b100;
initial BUSRD_STATE = ST_BUSRD_IDLE;
reg cpu_bus_rq2;
always @(posedge CLK) cpu_bus_rq2 <= cpu_bus_rq;
always @(posedge CLK) begin
if(CPU_STATE == ST_CPU_2
&& (op == OP_ST || op == OP_SWP)
&& op_param == 8'h03)
cpu_busdata <= cpu_idb;
else begin
case(BUSRD_STATE)
ST_BUSRD_IDLE: begin
if(cpu_bus_rq2) begin
BUSRD_STATE <= ST_BUSRD_WAIT;
end
end
ST_BUSRD_WAIT: begin
if(BUS_RDY) BUSRD_STATE <= ST_BUSRD_END;
else BUSRD_STATE <= ST_BUSRD_WAIT;
end
ST_BUSRD_END: begin
if(~cpu_busaddr[22]) cpu_busdata <= BUS_DI;
else cpu_busdata <= 8'h00;
end
endcase
end
end
// gpr write, either by CPU or by MMIO
always @(posedge CLK) begin
if(CPU_STATE == ST_CPU_2
&& (op == OP_ST || op == OP_SWP)
&& (op_param[7:4] == 4'h6)) begin
gpr[op_param[3:0]*3+2] <= cpu_idb[23:16];
gpr[op_param[3:0]*3+1] <= cpu_idb[15:8];
gpr[op_param[3:0]*3] <= cpu_idb[7:0];
end
else if(GPR_WR_EN) gpr[ADDR[5:0]] <= DIr;
end
/***************************
=========== MEM ===========
***************************/
cx4_datrom cx4_datrom (
.clka(CLK), // input clka
.wea(DATROM_WE), // input [0 : 0] wea
.addra(DATROM_ADDR), // input [9 : 0] addra
.dina(DATROM_DI), // input [23 : 0] dina
.clkb(CLK), // input clkb
.addrb(cx4_datrom_addr), // input [9 : 0] addrb
.doutb(cx4_datrom_do) // output [23 : 0] doutb
);
cx4_datram cx4_datram (
.clka(CLK), // input clka
.wea(DATRAM_WR_EN), // input [0 : 0] wea
.addra(ADDR[11:0]), // input [11 : 0] addra
.dina(DIr), // input [7 : 0] dina
.douta(DATRAM_DO), // output [7 : 0] douta
.clkb(CLK), // input clkb
.web(cx4_datram_we), // input [0 : 0] web
.addrb(cx4_datram_addr), // input [11 : 0] addrb
.dinb(cx4_datram_di), // input [7 : 0] dinb
.doutb(cx4_datram_do) // output [7 : 0] doutb
);
cx4_pgmrom cx4_pgmrom (
.clka(CLK), // input clka
.wea(cx4_pgmrom_we), // input [0 : 0] wea
.addra(cx4_pgmrom_addr), // input [9 : 0] addra
.dina(BUS_DI), // input [7 : 0] dina
.clkb(CLK), // input clkb
.addrb({cpu_page,cpu_pc}), // input [8 : 0] addrb
.doutb(cpu_op_w) // output [15 : 0] doutb
);
cx4_mul cx4_mul (
.clk(CLK), // input clk
.a(cpu_a), // input [23 : 0] a
.b(cpu_idb), // input [23 : 0] b
.p(cpu_mul_result) // output [47 : 0] p
);
endmodule