Add ppcpu as normal dir
diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 5a7fe33..0000000 --- a/.gitmodules +++ /dev/null
@@ -1,3 +0,0 @@ -[submodule "verilog/rtl/ppcpu"] - path = verilog/rtl/ppcpu - url = https://github.com/piotro888/ppcpu.git
diff --git a/verilog/rtl/ppcpu b/verilog/rtl/ppcpu deleted file mode 160000 index 74852c5..0000000 --- a/verilog/rtl/ppcpu +++ /dev/null
@@ -1 +0,0 @@ -Subproject commit 74852c5bd5d19da93a92226665813b39263b2405
diff --git a/verilog/rtl/ppcpu/rtl/config.v b/verilog/rtl/ppcpu/rtl/config.v new file mode 100644 index 0000000..967db59 --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/config.v
@@ -0,0 +1,49 @@ +// common values for cpu + +// register and data width bits +`ifndef CONFIG_H +`define CONFIG_H + +`define RW 16 + +`define REGNO 8 +`define REGNO_LOG 3 + +`define ADDR_W 16 +`define EXT_ADDR_W 24 + +`define I_SIZE 32 + +`define ADDR_BYTES 2 + +`define WB_ADDR_W 24 + +// -- ALU -- +`define ALU_MODE_W 4 +`define ALU_MODE_L_PASS `ALU_MODE_W'b0 +`define ALU_MODE_R_PASS `ALU_MODE_W'b1 +`define ALU_MODE_ADD `ALU_MODE_W'b10 +`define ALU_MODE_SUB `ALU_MODE_W'b11 +`define ALU_MODE_AND `ALU_MODE_W'b100 +`define ALU_MODE_OR `ALU_MODE_W'b101 +`define ALU_MODE_XOR `ALU_MODE_W'b110 +`define ALU_MODE_SHL `ALU_MODE_W'b111 +`define ALU_MODE_SHR `ALU_MODE_W'b1000 +`define ALU_MODE_MUL `ALU_MODE_W'b1001 +`define ALU_MODE_DIV `ALU_MODE_W'b1010 +`define ALU_MODE_ASHR `ALU_MODE_W'b1011 +`define ALU_MODE_SEXT `ALU_MODE_W'b1100 +`define ALU_MODE_MOD `ALU_MODE_W'b1101 +`define ALU_FLAG_W 3 +`define ALU_FLAG_CNT 5 +`define ALU_FLAG_Z `ALU_FLAG_W'b0 +`define ALU_FLAG_C `ALU_FLAG_W'b1 +`define ALU_FLAG_N `ALU_FLAG_W'b10 +`define ALU_FLAG_O `ALU_FLAG_W'b11 +`define ALU_FLAG_P `ALU_FLAG_W'b100 + +// -- SHARED DECODE -- +`define JUMP_CODE_W 5 // 4+1 +`define JUMP_CODE_BIT_EN 4 + +`endif \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/core/alu.v b/verilog/rtl/ppcpu/rtl/core/alu.v new file mode 100644 index 0000000..18ba7be --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/core/alu.v
@@ -0,0 +1,54 @@ +`include "config.v" + +module alu ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input [`RW-1:0] i_l, i_r, + output [`RW-1:0] o_out, + + input [`ALU_MODE_W-1:0] i_mode, + input wire i_carry, + + output reg [`ALU_FLAG_CNT-1:0] o_flags +); + +reg [`RW:0] outc; +assign o_out = outc[`RW-1:0]; + +always @* begin + case (i_mode) + default: + outc = {1'b0, i_l}; + `ALU_MODE_R_PASS: + outc = {1'b0, i_r}; + `ALU_MODE_ADD: + outc = i_l + i_r + {15'b0, i_carry}; + `ALU_MODE_SUB: + outc = i_l - i_r - {15'b0, i_carry}; + `ALU_MODE_AND: + outc = {1'b0, i_l & i_r}; + `ALU_MODE_OR: + outc = {1'b0, i_l | i_r}; + `ALU_MODE_XOR: + outc = {1'b0, i_l ^ i_r}; + `ALU_MODE_SHL: + outc = {1'b0, i_l} << {1'b0, i_r}; + `ALU_MODE_SHR: + outc = {1'b0, i_l >> i_r}; + `ALU_MODE_ASHR: + outc = {1'b0, (i_l >> i_r) | ({`RW{i_l[`RW-1]}} << (`RW-i_r))}; + `ALU_MODE_SEXT: + outc = {1'b0, {8{i_l[7]}}, i_l[7:0]}; + endcase + + o_flags[`ALU_FLAG_Z] = ~(|outc[`RW-1:0]); + o_flags[`ALU_FLAG_C] = outc[`RW]; + o_flags[`ALU_FLAG_N] = outc[`RW-1]; + o_flags[`ALU_FLAG_O] = (i_l[`RW-1] ^ (i_r[`RW-1]^(i_mode == `ALU_MODE_SUB))) & ((i_l[`RW-1]^outc[`RW-1])); + o_flags[`ALU_FLAG_P] = ^outc[`RW-1:0]; +end + +endmodule
diff --git a/verilog/rtl/ppcpu/rtl/core/alu_mul_div.v b/verilog/rtl/ppcpu/rtl/core/alu_mul_div.v new file mode 100644 index 0000000..a2d2703 --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/core/alu_mul_div.v
@@ -0,0 +1,77 @@ +`include "config.v" + +module alu_mul_div ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input i_clk, + input i_rst, + + input [`RW-1:0] i_a, i_b, + output [`RW-1:0] o_d, + + input i_mul, + input i_div, + input i_mod, + + input i_submit, + input i_flush, + output o_busy +); + +`define RWLOG 4 + +reg [`RWLOG-1:0] cbit; +reg comp; + +always @(posedge i_clk) begin + if (i_rst | i_flush) begin + comp <= 1'b0; + end else if (i_submit) begin + cbit <= (i_mul ? `RWLOG'b1 : `RWLOG'b0); + comp <= 1'b1; + end else if (comp) begin + cbit <= cbit+1'b1; + if (cbit == `RWLOG'd15) begin + comp <= 1'b0; + end + end +end + +reg [`RW-1:0] mul_res; + +always @(posedge i_clk) begin + if (i_submit & i_mul) begin + mul_res <= (i_b[0] ? i_a : `RW'b0); + end else if (comp & i_b[cbit] & i_mul) begin + mul_res <= mul_res + (i_a<<cbit); + end +end + +reg [`RW-1:0] div_res, div_cur; +wire [`RW-1:0] div_diff = div_cur-i_b; + +always @(posedge i_clk) begin + if (i_submit & (i_div | i_mod)) begin + div_res <= `RW'b0; + div_cur <= {{`RW-1{1'b0}}, i_a[`RW-1]}; + end else if (comp & (i_div | i_mod)) begin + if (div_cur >= i_b) begin + div_res[`RW-cbit-1] <= 1'b1; + if (cbit != `RWLOG'd15) + div_cur <= {div_diff[14:0], i_a[`RW-cbit-2]}; + else + div_cur <= div_diff; + end else begin + if (cbit != `RWLOG'd15) + div_cur <= {div_cur[14:0], i_a[`RW-cbit-2]}; + end + end +end + +assign o_d = (i_mod ? div_cur : (i_div ? div_res : mul_res)); +assign o_busy = (i_submit | comp); + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/core/core.v b/verilog/rtl/ppcpu/rtl/core/core.v new file mode 100644 index 0000000..9c8a8ec --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/core/core.v
@@ -0,0 +1,282 @@ +`include "config.v" + +module core #(parameter CORENO = 0, INT_VEC = 1) ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input i_clk, + input i_rst, + input i_disable, + + // fetch input singals + output [`RW-1:0] o_req_addr, + output o_req_active, o_req_ppl_submit, + input [`I_SIZE-1:0] i_req_data, + input i_req_data_valid, + + output [`RW-1:0] dbg_r0, dbg_pc, + + // data memory connections + output [`RW-1:0] o_mem_addr, o_mem_data, + input [`RW-1:0] i_mem_data, + output o_mem_req, o_mem_we, + input i_mem_ack, + output [`ADDR_BYTES-1:0] o_mem_sel, + output o_mem_long, + output [7:0] o_mem_addr_high, + + input i_irq, + output o_c_instr_page, o_c_data_page, + output [`RW-1:0] sr_bus_addr, sr_bus_data_o, + output sr_bus_we, + output o_icache_flush, + input i_mem_exception, + input i_mc_core_int, + input [`RW-1:0] i_core_int_sreg, + output o_c_instr_long, + output [7:0] o_instr_long_addr +); + +// --- CPU INTERNAL CONNECTIONS --- + +wire fetch_decode_next_ready; +wire fetch_decode_submit; +wire [`I_SIZE-1:0] fetch_decode_d_instr; +wire fetch_decode_jmp_pred; +wire execute_fetch_pc_update; +wire [`RW-1:0] execute_fetch_pc; +wire fde_pipeline_flush; + +// Pipeline stage 0 - FETCH +fetch fetch ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), .i_rst(i_rst), .mem_addr(o_req_addr), .mem_submit(o_req_ppl_submit), + .mem_data(i_req_data), .mem_ack(i_req_data_valid), .i_next_ready(fetch_decode_next_ready), + .o_submit(fetch_decode_submit), .o_instr(fetch_decode_d_instr), .o_jmp_predict(fetch_decode_jmp_pred), + .i_exec_pc(execute_fetch_pc), .i_flush(fde_pipeline_flush)); +assign o_req_active = ~i_disable; + +wire decode_execute_next_ready; +wire decode_execute_submit; +wire [`I_SIZE-17:0] de_imm_pass; +wire de_jmp_pred; + +wire dec_pc_inc, dec_pc_ie; +wire dec_r_bus_imm; +wire [`ALU_MODE_W-1:0] dec_alu_mode; +wire dec_alu_carry_en, dec_alu_flags_ie; +wire [`REGNO_LOG-1:0] dec_l_reg_sel, dec_r_reg_sel; +wire [`REGNO-1:0] dec_rf_ie; +wire [`JUMP_CODE_W-1:0] dec_jump_cond_code; +wire dec_mem_access, dec_mem_we, dec_mem_width; +wire [1:0] dec_used_operands; +wire dec_sreg_load, dec_sreg_store, dec_sreg_jal_over, dec_sreg_irt, dec_sys, dec_mem_long; + +// Pipeline stage 1 - DECODE +decode decode ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), .i_rst(i_rst), .o_ready(fetch_decode_next_ready), .o_submit(decode_execute_submit), + .i_next_ready(decode_execute_next_ready), .i_instr_l(fetch_decode_d_instr[15:0]), .i_imm_pass(fetch_decode_d_instr[`I_SIZE-1:16]), + .o_imm_pass(de_imm_pass), .oc_pc_inc(dec_pc_inc), .oc_pc_ie(dec_pc_ie), .oc_r_bus_imm(dec_r_bus_imm), .oc_alu_mode(dec_alu_mode), + .oc_alu_flags_ie(dec_alu_flags_ie), .oc_alu_carry_en(dec_alu_carry_en), .oc_l_reg_sel(dec_l_reg_sel), .oc_r_reg_sel(dec_r_reg_sel), + .oc_rf_ie(dec_rf_ie), .i_submit(fetch_decode_submit), .oc_jump_cond_code(dec_jump_cond_code), .i_jmp_pred_pass(fetch_decode_jmp_pred), + .o_jmp_pred_pass(de_jmp_pred), .i_flush(fde_pipeline_flush), .oc_mem_access(dec_mem_access), .oc_mem_we(dec_mem_we), + .oc_used_operands(dec_used_operands), .oc_sreg_load(dec_sreg_load), .oc_sreg_store(dec_sreg_store), .oc_sreg_jal_over(dec_sreg_jal_over), + .oc_sreg_irt(dec_sreg_irt), .oc_sys(dec_sys), .oc_mem_width(dec_mem_width), .oc_mem_long(dec_mem_long)); + +wire [`RW-1:0] ew_data; +wire [`RW-1:0] ew_addr; +wire [`REGNO-1:0] ew_reg_ie; +wire ew_mem_access, ew_mem_we, ew_mem_width; +wire [`REGNO-1:0] we_reg_ie; +wire [`RW-1:0] we_reg_data; +wire ew_submit; +wire ew_next_ready; +wire ew_long_mode; +wire [7:0] ew_addr_high; + +// Pipeline stage 2 - EXECUTE +execute #(.CORENO(CORENO), .INT_VEC(INT_VEC)) execute( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), .i_rst(i_rst), .o_ready(decode_execute_next_ready), .i_imm(de_imm_pass), .c_pc_inc(dec_pc_inc), + .c_pc_ie(dec_pc_ie), .c_r_bus_imm(dec_r_bus_imm), .c_alu_mode(dec_alu_mode), .c_alu_flags_ie(dec_alu_flags_ie), + .c_alu_carry_en(dec_alu_carry_en), .c_l_reg_sel(dec_l_reg_sel), .c_r_reg_sel(dec_r_reg_sel), .c_rf_ie(dec_rf_ie), + .dbg_pc(dbg_pc), .dbg_r0(dbg_r0), .i_submit(decode_execute_submit), .c_jump_cond_code(dec_jump_cond_code), .o_pc_update(execute_fetch_pc_update), + .o_exec_pc(execute_fetch_pc), .i_jmp_predict(de_jmp_pred), .i_flush(fde_pipeline_flush), .o_flush(fde_pipeline_flush), .c_mem_access(dec_mem_access), + .c_mem_we(dec_mem_we), .o_data(ew_data), .o_addr(ew_addr), .o_reg_ie(ew_reg_ie), .o_mem_access(ew_mem_access), .o_mem_we(ew_mem_we), .o_submit(ew_submit), + .i_next_ready(ew_next_ready), .i_reg_ie(we_reg_ie), .i_reg_data(we_reg_data), .c_used_operands(dec_used_operands), .c_sreg_load(dec_sreg_load), + .c_sreg_store(dec_sreg_store), .c_sreg_jal_over(dec_sreg_jal_over), .i_irq(i_irq), .c_sreg_irt(dec_sreg_irt), .o_c_instr_page(o_c_instr_page), + .sr_bus_addr(sr_bus_addr), .sr_bus_data_o(sr_bus_data_o), .sr_bus_we(sr_bus_we), .o_icache_flush(o_icache_flush), .c_sys(dec_sys), .c_mem_width(dec_mem_width), + .o_mem_width(ew_mem_width), .o_c_data_page(o_c_data_page), .i_mem_exception(i_mem_exception), + .i_core_int(i_mc_core_int), .i_core_int_sreg(i_core_int_sreg), .o_c_instr_long_mode(o_c_instr_long), .o_instr_addr_high(o_instr_long_addr), .c_mem_long(dec_mem_long), + .o_mem_long_mode(ew_long_mode), .o_mem_addr_high(ew_addr_high)); + +// Pipeline stage 3 - MEM&WB +memwb memwb( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), .i_rst(i_rst), .i_data(ew_data), .i_addr(ew_addr), .i_reg_ie(ew_reg_ie), .i_mem_access(ew_mem_access), .i_mem_we(ew_mem_we), + .o_reg_ie(we_reg_ie), .o_reg_data(we_reg_data), .i_submit(ew_submit), .o_ready(ew_next_ready), .o_mem_req(o_mem_req), .o_mem_addr(o_mem_addr), + .o_mem_data(o_mem_data), .o_mem_we(o_mem_we), .i_mem_data(i_mem_data), .i_mem_ack(i_mem_ack), .i_mem_width(ew_mem_width), .o_mem_sel(o_mem_sel), + .o_mem_exception(i_mem_exception), .i_addr_high(ew_addr_high), .i_mem_long(ew_long_mode), .o_mem_long(o_mem_long), .o_mem_addr_high(o_mem_addr_high)); + +endmodule + +module core0 ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input i_clk, + input i_rst, + input i_disable, + + // fetch input singals + output [`RW-1:0] o_req_addr, + output o_req_active, o_req_ppl_submit, + input [`I_SIZE-1:0] i_req_data, + input i_req_data_valid, + + output [`RW-1:0] dbg_r0, dbg_pc, + + // data memory connections + output [`RW-1:0] o_mem_addr, o_mem_data, + input [`RW-1:0] i_mem_data, + output o_mem_req, o_mem_we, + input i_mem_ack, + output [`ADDR_BYTES-1:0] o_mem_sel, + output o_mem_long, + output [7:0] o_mem_addr_high, + + input i_irq, + output o_c_instr_page, o_c_data_page, + output [`RW-1:0] sr_bus_addr, sr_bus_data_o, + output sr_bus_we, + output o_icache_flush, + input i_mem_exception, + input i_mc_core_int, + input [`RW-1:0] i_core_int_sreg, + output o_c_instr_long, + output [7:0] o_instr_long_addr +); + +core #(.CORENO(0), .INT_VEC(1)) core_0 ( + .i_clk(i_clk), + .i_rst(i_rst), + .i_disable(i_disable), + .o_req_addr(o_req_addr), + .o_req_active(o_req_active), + .o_req_ppl_submit(o_req_ppl_submit), + .i_req_data(i_req_data), + .i_req_data_valid(i_req_data_valid), + .dbg_r0(dbg_r0), + .dbg_pc(dbg_pc), + .o_mem_addr(o_mem_addr), + .o_mem_data(o_mem_data), + .i_mem_data(i_mem_data), + .o_mem_req(o_mem_req), + .o_mem_we(o_mem_we), + .i_mem_ack(i_mem_ack), + .o_mem_sel(o_mem_sel), + .o_mem_long(o_mem_long), + .o_mem_addr_high(o_mem_addr_high), + .i_irq(i_irq), + .o_c_instr_page(o_c_instr_page), + .o_c_data_page(o_c_data_page), + .sr_bus_addr(sr_bus_addr), + .sr_bus_data_o(sr_bus_data_o), + .sr_bus_we(sr_bus_we), + .o_icache_flush(o_icache_flush), + .i_mem_exception(i_mem_exception), + .i_mc_core_int(i_mc_core_int), + .i_core_int_sreg(i_core_int_sreg), + .o_c_instr_long(o_c_instr_long), + .o_instr_long_addr(o_instr_long_addr) +); + +endmodule + +module core1 ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input i_clk, + input i_rst, + input i_disable, + + // fetch input singals + output [`RW-1:0] o_req_addr, + output o_req_active, o_req_ppl_submit, + input [`I_SIZE-1:0] i_req_data, + input i_req_data_valid, + + output [`RW-1:0] dbg_r0, dbg_pc, + + // data memory connections + output [`RW-1:0] o_mem_addr, o_mem_data, + input [`RW-1:0] i_mem_data, + output o_mem_req, o_mem_we, + input i_mem_ack, + output [`ADDR_BYTES-1:0] o_mem_sel, + output o_mem_long, + output [7:0] o_mem_addr_high, + + input i_irq, + output o_c_instr_page, o_c_data_page, + output [`RW-1:0] sr_bus_addr, sr_bus_data_o, + output sr_bus_we, + output o_icache_flush, + input i_mem_exception, + input i_mc_core_int, + input [`RW-1:0] i_core_int_sreg, + output o_c_instr_long, + output [7:0] o_instr_long_addr +); + +core #(.CORENO(1), .INT_VEC(2)) core_1 ( + .i_clk(i_clk), + .i_rst(i_rst), + .i_disable(i_disable), + .o_req_addr(o_req_addr), + .o_req_active(o_req_active), + .o_req_ppl_submit(o_req_ppl_submit), + .i_req_data(i_req_data), + .i_req_data_valid(i_req_data_valid), + .dbg_r0(dbg_r0), + .dbg_pc(dbg_pc), + .o_mem_addr(o_mem_addr), + .o_mem_data(o_mem_data), + .i_mem_data(i_mem_data), + .o_mem_req(o_mem_req), + .o_mem_we(o_mem_we), + .i_mem_ack(i_mem_ack), + .o_mem_sel(o_mem_sel), + .o_mem_long(o_mem_long), + .o_mem_addr_high(o_mem_addr_high), + .i_irq(i_irq), + .o_c_instr_page(o_c_instr_page), + .o_c_data_page(o_c_data_page), + .sr_bus_addr(sr_bus_addr), + .sr_bus_data_o(sr_bus_data_o), + .sr_bus_we(sr_bus_we), + .o_icache_flush(o_icache_flush), + .i_mem_exception(i_mem_exception), + .i_mc_core_int(i_mc_core_int), + .i_core_int_sreg(i_core_int_sreg), + .o_c_instr_long(o_c_instr_long), + .o_instr_long_addr(o_instr_long_addr) +); + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/core/decode.v b/verilog/rtl/ppcpu/rtl/core/decode.v new file mode 100644 index 0000000..8ac65d3 --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/core/decode.v
@@ -0,0 +1,524 @@ +`include "config.v" + +module decode ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input wire i_clk, + input wire i_rst, + + input [15:0] i_instr_l, + input [`I_SIZE-17:0] i_imm_pass, + output reg [`I_SIZE-17:0] o_imm_pass, + input i_jmp_pred_pass, + output reg o_jmp_pred_pass, + + // Pipeline control + input i_next_ready, + input i_submit, + output o_ready, + output reg o_submit, + input i_flush, + + output reg oc_pc_inc, oc_pc_ie, + output reg oc_r_bus_imm, + output reg [`ALU_MODE_W-1:0] oc_alu_mode, + output reg oc_alu_carry_en, oc_alu_flags_ie, + output reg [`REGNO_LOG-1:0] oc_l_reg_sel, oc_r_reg_sel, + output reg [`REGNO-1:0] oc_rf_ie, + output reg [`JUMP_CODE_W-1:0] oc_jump_cond_code, + output reg oc_mem_access, oc_mem_we, oc_mem_width, + output reg [1:0] oc_used_operands, + output reg oc_sreg_load, oc_sreg_store, oc_sreg_jal_over, oc_sreg_irt, oc_sys, + output reg oc_mem_long, + + output dbg_out +); + +// COMBINATIONAL INSTRUCTION DECODER + +`define OPC_NOP 7'h00 +`define OPC_MOV 7'h01 +`define OPC_LDD 7'h02 +`define OPC_LDO 7'h03 +`define OPC_LDI 7'h04 +`define OPC_STD 7'h05 +`define OPC_STO 7'h06 +`define OPC_ADD 7'h07 +`define OPC_ADI 7'h08 +`define OPC_ADC 7'h09 +`define OPC_SUB 7'h0a +`define OPC_SUC 7'h0b +`define OPC_CMP 7'h0c +`define OPC_CMI 7'h0d +`define OPC_JMP 7'h0e +`define OPC_JAL 7'h0f +`define OPC_SRL 7'h10 +`define OPC_SRS 7'h11 +`define OPC_SYS 7'h12 +`define OPC_AND 7'h13 +`define OPC_ORR 7'h14 +`define OPC_XOR 7'h15 +`define OPC_ANI 7'h16 +`define OPC_ORI 7'h17 +`define OPC_XOI 7'h18 +`define OPC_SHL 7'h19 +`define OPC_SHR 7'h1a +`define OPC_CAI 7'h1b +`define OPC_MUL 7'h1c // Temporary instructions (MUL,DIV) to be removed after compiler patch +`define OPC_DIV 7'h1d +`define OPC_IRT 7'h1e +`define OPC_LD8 7'h1f +`define OPC_LO8 7'h20 +`define OPC_SD8 7'h21 +`define OPC_SO8 7'h22 +`define OPC_SLI 7'h23 +`define OPC_SRI 7'h24 +`define OPC_SAR 7'h25 +`define OPC_SAI 7'h26 +`define OPC_SEX 7'h27 +`define OPC_LLO 7'h28 +`define OPC_SLO 7'h29 +`define OPC_LL8 7'h2a +`define OPC_SL8 7'h2b +`define OPC_MOD 7'h2c +// sreg imm?? + +wire [6:0] opcode = i_instr_l[6:0]; +wire [2:0] reg_dst = i_instr_l[9:7]; +wire [2:0] reg_st = i_instr_l[12:10]; +wire [2:0] reg_nd = i_instr_l[15:13]; + +// Comb output signals +reg c_pc_inc, c_pc_ie; +reg c_r_bus_imm; +reg [`ALU_MODE_W-1:0] c_alu_mode; +reg c_alu_carry_en, c_alu_flags_ie; +reg [`REGNO_LOG-1:0] c_l_reg_sel, c_r_reg_sel; +reg [`REGNO-1:0] c_rf_ie; +reg [`JUMP_CODE_W-1:0] c_jump_cond_code; +reg c_mem_access, c_mem_we, c_mem_width; +reg [1:0] c_used_operands; +reg c_sreg_load, c_sreg_store, c_sreg_jal_over, c_sreg_irt, c_sys, c_mem_long; + +always @(*) begin + // defaults + c_pc_inc = 1'b1; + {c_pc_ie, c_r_bus_imm, c_alu_carry_en, c_alu_flags_ie, c_mem_access, + c_mem_we, c_sreg_load, c_sreg_store, c_sreg_jal_over, c_sreg_irt, c_sys, c_mem_width, c_mem_long} = 13'b0; + c_rf_ie = `REGNO'b0; + c_alu_mode = `ALU_MODE_W'b0; + c_l_reg_sel = `REGNO_LOG'b0; + c_r_reg_sel = `REGNO_LOG'b0; + c_jump_cond_code = `JUMP_CODE_W'b0; + c_used_operands = 2'b0; + + case (opcode) + `OPC_NOP: begin + end + `OPC_MOV: begin + c_alu_mode = `ALU_MODE_L_PASS; + c_l_reg_sel = reg_st; + c_rf_ie[reg_dst] = 1'b1; + c_used_operands = 2'b01; + end + `OPC_LDD: begin + c_r_bus_imm = 1'b1; + c_alu_mode = `ALU_MODE_R_PASS; + c_rf_ie[reg_dst] = 1'b1; + c_mem_access = 1'b1; + end + `OPC_LDO: begin + c_l_reg_sel = reg_st; + c_r_bus_imm = 1'b1; + c_alu_mode = `ALU_MODE_ADD; + c_rf_ie[reg_dst] = 1'b1; + c_mem_access = 1'b1; + c_used_operands = 2'b01; + end + `OPC_LDI: begin + c_alu_mode = `ALU_MODE_R_PASS; + c_r_bus_imm = 1'b1; + c_rf_ie[reg_dst] = 1'b1; + end + `OPC_STD: begin + c_r_bus_imm = 1'b1; + c_alu_mode = `ALU_MODE_R_PASS; + c_r_reg_sel = reg_st; + c_mem_access = 1'b1; + c_mem_we = 1'b1; + c_used_operands = 2'b10; + end + `OPC_STO: begin + c_l_reg_sel = reg_nd; + c_r_bus_imm = 1'b1; + c_alu_mode = `ALU_MODE_ADD; + c_r_reg_sel = reg_st; + c_mem_access = 1'b1; + c_mem_we = 1'b1; + c_used_operands = 2'b11; + end + `OPC_ADD: begin + c_alu_mode = `ALU_MODE_ADD; + c_l_reg_sel = reg_st; + c_r_reg_sel = reg_nd; + c_rf_ie[reg_dst] = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b11; + end + `OPC_ADI: begin + c_alu_mode = `ALU_MODE_ADD; + c_l_reg_sel = reg_st; + c_r_bus_imm = 1'b1; + c_rf_ie[reg_dst] = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b01; + end + `OPC_ADC: begin + c_alu_mode = `ALU_MODE_ADD; + c_l_reg_sel = reg_st; + c_r_reg_sel = reg_nd; + c_alu_carry_en = 1'b1; + c_rf_ie[reg_dst] = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b11; + end + `OPC_SUB: begin + c_alu_mode = `ALU_MODE_SUB; + c_l_reg_sel = reg_st; + c_r_reg_sel = reg_nd; + c_rf_ie[reg_dst] = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b11; + end + `OPC_SUC: begin + c_alu_mode = `ALU_MODE_SUB; + c_l_reg_sel = reg_st; + c_r_reg_sel = reg_nd; + c_r_bus_imm = 1'b1; + c_alu_carry_en = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b11; + end + `OPC_CMP: begin + c_alu_mode = `ALU_MODE_SUB; + c_l_reg_sel = reg_st; + c_r_reg_sel = reg_nd; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b11; + end + `OPC_CMI: begin + c_alu_mode = `ALU_MODE_SUB; + c_l_reg_sel = reg_st; + c_r_bus_imm = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b01; + end + `OPC_JMP: begin + // NOTE: Conditional jumps are decoded in execute stage + // to not unnecessarily stall the pipeline each time prediction + // is taken + c_pc_inc = 1'b0; + c_pc_ie = 1'b0; + c_alu_mode = `ALU_MODE_R_PASS; + c_r_bus_imm = 1'b1; + c_jump_cond_code = {1'b1, i_instr_l[10:7]}; + end + `OPC_JAL: begin + c_pc_ie = 1'b0; + c_pc_inc = 1'b0; + c_alu_mode = `ALU_MODE_R_PASS; + c_r_bus_imm = 1'b1; + c_sreg_jal_over = 1'b1; + c_rf_ie[reg_dst] = 1'b1; + // set jump code to unconditional jump, pred is set by fetch + c_jump_cond_code = {1'b1, 4'b0}; + end + `OPC_SRL: begin + c_sreg_load = 1'b1; + c_rf_ie[reg_dst] = 1'b1; + end + `OPC_SRS: begin + c_r_reg_sel = reg_st; + c_sreg_store = 1'b1; + c_used_operands = 2'b10; + end + `OPC_AND: begin + c_alu_mode = `ALU_MODE_AND; + c_l_reg_sel = reg_st; + c_r_reg_sel = reg_nd; + c_rf_ie[reg_dst] = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b11; + end + `OPC_ORR: begin + c_alu_mode = `ALU_MODE_OR; + c_l_reg_sel = reg_st; + c_r_reg_sel = reg_nd; + c_rf_ie[reg_dst] = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b11; + end + `OPC_XOR: begin + c_alu_mode = `ALU_MODE_XOR; + c_l_reg_sel = reg_st; + c_r_reg_sel = reg_nd; + c_rf_ie[reg_dst] = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b11; + end + `OPC_ANI: begin + c_alu_mode = `ALU_MODE_AND; + c_l_reg_sel = reg_st; + c_r_bus_imm = 1'b1; + c_rf_ie[reg_dst] = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b01; + end + `OPC_ORI: begin + c_alu_mode = `ALU_MODE_OR; + c_l_reg_sel = reg_st; + c_r_bus_imm = 1'b1; + c_rf_ie[reg_dst] = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b01; + end + `OPC_XOI: begin + c_alu_mode = `ALU_MODE_XOR; + c_l_reg_sel = reg_st; + c_r_bus_imm = 1'b1; + c_rf_ie[reg_dst] = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b01; + end + `OPC_SHL: begin + c_alu_mode = `ALU_MODE_SHL; + c_l_reg_sel = reg_st; + c_r_reg_sel = reg_nd; + c_rf_ie[reg_dst] = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b11; + end + `OPC_SHR: begin + c_alu_mode = `ALU_MODE_SHR; + c_l_reg_sel = reg_st; + c_r_reg_sel = reg_nd; + c_rf_ie[reg_dst] = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b11; + end + `OPC_CAI: begin + c_alu_mode = `ALU_MODE_AND; + c_l_reg_sel = reg_st; + c_r_bus_imm = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b01; + end + `OPC_MUL: begin + c_alu_mode = `ALU_MODE_MUL; + c_l_reg_sel = reg_st; + c_r_reg_sel = reg_nd; + c_rf_ie[reg_dst] = 1'b1; + c_used_operands = 2'b11; + end + `OPC_DIV: begin + c_alu_mode = `ALU_MODE_DIV; + c_l_reg_sel = reg_st; + c_r_reg_sel = reg_nd; + c_rf_ie[reg_dst] = 1'b1; + c_used_operands = 2'b11; + end + `OPC_SYS: begin + c_sys = 1'b1; + end + `OPC_IRT: begin + c_sreg_irt = 1'b1; + c_pc_ie = 1'b0; + c_pc_inc = 1'b0; + end + `OPC_LD8: begin + c_r_bus_imm = 1'b1; + c_alu_mode = `ALU_MODE_R_PASS; + c_rf_ie[reg_dst] = 1'b1; + c_mem_access = 1'b1; + c_mem_width = 1'b1; + end + `OPC_LO8: begin + c_l_reg_sel = reg_st; + c_r_bus_imm = 1'b1; + c_alu_mode = `ALU_MODE_ADD; + c_rf_ie[reg_dst] = 1'b1; + c_mem_access = 1'b1; + c_used_operands = 2'b01; + c_mem_width = 1'b1; + end + `OPC_SD8: begin + c_r_bus_imm = 1'b1; + c_alu_mode = `ALU_MODE_R_PASS; + c_r_reg_sel = reg_st; + c_mem_access = 1'b1; + c_mem_we = 1'b1; + c_used_operands = 2'b10; + c_mem_width = 1'b1; + end + `OPC_SO8: begin + c_l_reg_sel = reg_nd; + c_r_bus_imm = 1'b1; + c_alu_mode = `ALU_MODE_ADD; + c_r_reg_sel = reg_st; + c_mem_access = 1'b1; + c_mem_we = 1'b1; + c_used_operands = 2'b11; + c_mem_width = 1'b1; + end + `OPC_SLI: begin + c_alu_mode = `ALU_MODE_SHL; + c_l_reg_sel = reg_st; + c_r_bus_imm = 1'b1; + c_rf_ie[reg_dst] = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b01; + end + `OPC_SRI: begin + c_alu_mode = `ALU_MODE_SHR; + c_l_reg_sel = reg_st; + c_r_bus_imm = 1'b1; + c_rf_ie[reg_dst] = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b01; + end + `OPC_SAR: begin + c_alu_mode = `ALU_MODE_ASHR; + c_l_reg_sel = reg_st; + c_r_reg_sel = reg_nd; + c_rf_ie[reg_dst] = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b11; + end + `OPC_SAI: begin + c_alu_mode = `ALU_MODE_ASHR; + c_l_reg_sel = reg_st; + c_r_bus_imm = 1'b1; + c_rf_ie[reg_dst] = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b01; + end + `OPC_SEX: begin + c_alu_mode = `ALU_MODE_SEXT; + c_l_reg_sel = reg_st; + c_rf_ie[reg_dst] = 1'b1; + c_alu_flags_ie = 1'b1; + c_used_operands = 2'b01; + end + `OPC_LLO: begin + c_l_reg_sel = reg_st; + c_r_bus_imm = 1'b1; + c_alu_mode = `ALU_MODE_ADD; + c_rf_ie[reg_dst] = 1'b1; + c_mem_access = 1'b1; + c_mem_long = 1'b1; + c_used_operands = 2'b01; + end + `OPC_SLO: begin + c_l_reg_sel = reg_nd; + c_r_bus_imm = 1'b1; + c_alu_mode = `ALU_MODE_ADD; + c_r_reg_sel = reg_st; + c_mem_access = 1'b1; + c_mem_we = 1'b1; + c_mem_long = 1'b1; + c_used_operands = 2'b11; + end + `OPC_LL8: begin + c_l_reg_sel = reg_st; + c_r_bus_imm = 1'b1; + c_alu_mode = `ALU_MODE_ADD; + c_rf_ie[reg_dst] = 1'b1; + c_mem_access = 1'b1; + c_used_operands = 2'b01; + c_mem_long = 1'b1; + c_mem_width = 1'b1; + end + `OPC_SL8: begin + c_l_reg_sel = reg_nd; + c_r_bus_imm = 1'b1; + c_alu_mode = `ALU_MODE_ADD; + c_r_reg_sel = reg_st; + c_mem_access = 1'b1; + c_mem_we = 1'b1; + c_used_operands = 2'b11; + c_mem_long = 1'b1; + c_mem_width = 1'b1; + end + `OPC_MOD: begin + c_alu_mode = `ALU_MODE_MOD; + c_l_reg_sel = reg_st; + c_r_reg_sel = reg_nd; + c_rf_ie[reg_dst] = 1'b1; + c_used_operands = 2'b11; + end + default: begin + end + endcase +end + + +// PIPELINE WRAPPER +reg input_valid; + +// ready signal must be combinational to be registered on time by previous stage +assign o_ready = i_next_ready & ~input_valid; + +always @(posedge i_clk) begin + if(i_rst) begin + o_submit <= 1'b0; + input_valid <= 1'b0; + end else begin + // default bubble + o_submit <= 1'b0; + + if (i_next_ready & (i_submit | input_valid) & ~i_flush) begin + o_submit <= 1'b1; + input_valid <= 1'b0; + + o_imm_pass <= i_imm_pass; + o_jmp_pred_pass <= i_jmp_pred_pass; + // Submit control signals + oc_pc_inc <= c_pc_inc; + oc_pc_ie <= c_pc_ie; + oc_r_bus_imm <= c_r_bus_imm; + oc_alu_carry_en <= c_alu_carry_en; + oc_alu_flags_ie <= c_alu_flags_ie; + oc_rf_ie <= c_rf_ie; + oc_alu_mode <= c_alu_mode; + oc_l_reg_sel <= c_l_reg_sel; + oc_r_reg_sel <= c_r_reg_sel; + oc_jump_cond_code <= c_jump_cond_code; + oc_mem_access <= c_mem_access; + oc_mem_we <= c_mem_we; + oc_mem_width <= c_mem_width; + oc_used_operands <= c_used_operands; + oc_sreg_load <= c_sreg_load; + oc_sreg_store <= c_sreg_store; + oc_sreg_jal_over <= c_sreg_jal_over; + oc_sreg_irt <= c_sreg_irt; + oc_sys <= c_sys; + oc_mem_long <= c_mem_long; + end + + if (i_submit & ~i_next_ready & ~i_flush) begin + input_valid <= 1'b1; // don't overwrite buffer + end + + if (i_flush) begin + o_submit <= 1'b0; + input_valid <= 1'b0; + end + end +end + +assign dbg_out = o_ready; + +endmodule
diff --git a/verilog/rtl/ppcpu/rtl/core/execute.v b/verilog/rtl/ppcpu/rtl/core/execute.v new file mode 100644 index 0000000..1f6a8b4 --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/core/execute.v
@@ -0,0 +1,483 @@ +`include "config.v" + +module execute #(parameter CORENO = 0, INT_VEC = 1) ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input i_clk, + input i_rst, + + // Pipeline control singnals + output o_ready, + input i_submit, + output reg o_flush, + input i_flush, + + input [`RW-1:0] i_imm, + input i_jmp_predict, + + // Execution control singals + input c_pc_inc, c_pc_ie, + input c_r_bus_imm, + input [`ALU_MODE_W-1:0] c_alu_mode, + input c_alu_carry_en, c_alu_flags_ie, + input [`REGNO_LOG-1:0] c_l_reg_sel, c_r_reg_sel, + input [`REGNO-1:0] c_rf_ie, + input [`JUMP_CODE_W-1:0] c_jump_cond_code, + input c_mem_access, c_mem_we, c_mem_width, + input [1:0] c_used_operands, + input c_sreg_load, c_sreg_store, c_sreg_jal_over, c_sreg_irt, c_sys, c_mem_long, + + // Signals to fetch stage to handle mispredictions + output o_pc_update, + output [`RW-1:0] o_exec_pc, + + // Debug outputs + output [`RW-1:0] dbg_r0, dbg_pc, + + // Pipeline next stage + output reg [`RW-1:0] o_data, + output reg [`RW-1:0] o_addr, + output reg [`REGNO-1:0] o_reg_ie, + output reg o_mem_access, o_mem_we, o_mem_width, + output reg o_submit, + input i_next_ready, + input [`REGNO-1:0] i_reg_ie, + input [`RW-1:0] i_reg_data, + + input i_irq, + output o_c_instr_page, + output reg o_c_data_page, + output [`RW-1:0] sr_bus_addr, sr_bus_data_o, + output sr_bus_we, + output reg o_icache_flush, + input i_mem_exception, + input i_core_int, + input [`RW-1:0] i_core_int_sreg, + output o_c_instr_long_mode, + output reg o_mem_long_mode, + output [7:0] o_instr_addr_high, + output reg [7:0] o_mem_addr_high +); + +reg next_ready_delayed; +// detect RAW pipeline hazard +wire raw_hazard = ( + (c_used_operands[0] & o_reg_ie[c_l_reg_sel]) | + (c_used_operands[1] & o_reg_ie[c_r_reg_sel]) | + (c_mem_long & sreg_long_ptr_en & c_used_operands[0] & o_reg_ie[c_l_reg_sel+1]) + ) & (o_submit | ~next_ready_delayed); +// hazard happens also in the first cycle when next_ready becomes high, delayed signal is used + +wire i_invalidate = i_flush | irq | pc_high_updated; +// hazard doesn't invalidate instructions, only holds it +wire hold_req = raw_hazard | alu_mul_busy; + +wire i_valid = i_submit & ~i_invalidate; +reg hold_valid; + +wire instr_valid = i_valid | (hold_valid & ~i_submit & ~i_invalidate); +wire exec_submit = i_next_ready & instr_valid & ~hold_req; + +// don't update state when current instruction is not valid (flush or bubble) +assign o_ready = exec_submit | ~instr_valid; + +// At IRQ, current instruction (and state update) is invalidated and pc is saved to sr, to +// continue execution from current instruction. Flush is requested on next cycle +wire irq = ((i_irq | i_core_int) & irq_en) | prev_sys | trap_exception | i_mem_exception; +// core_int is masked as externel event to not interrupt irq handler + +always @(posedge i_clk) begin + if(i_rst) begin + hold_valid <= 1'b0; + end else if (i_invalidate | exec_submit) begin + hold_valid <= 1'b0; + end else if (i_valid) begin + hold_valid <= 1'b1; + end +end + +always @(posedge i_clk) begin + if(i_rst) begin + next_ready_delayed <= 1'b0; + end else begin + next_ready_delayed <= i_next_ready; + end +end + +// Internal buses +wire [`RW-1:0] reg_l_con, reg_r_con; +wire [`RW-1:0] alu_l_bus, alu_r_bus; +wire [`RW-1:0] alu_bus; + +// Muxes definitions +assign alu_l_bus = reg_l_con; +assign alu_r_bus = (c_r_bus_imm ? i_imm : reg_r_con); +assign alu_bus = (mul_div_op ? alu_mul_res : alu_res); + +// Component connects +wire [`RW-1:0] pc_val; +wire [`ALU_FLAG_CNT-1:0] alu_flags_d, alu_flags_q; +assign dbg_pc = pc_val; +assign o_pc_update = exec_submit; +assign o_exec_pc = pc_val; +wire [`RW-1:0] sreg_in = reg_r_con; +reg [`RW-1:0] sreg_out; +wire [`RW-1:0] dbg_reg_out; +wire pc_overflow; +wire [`RW-1:0] reg_l_high_con; +wire [`RW-1:0] alu_mul_res, alu_res; +wire alu_mul_busy; +wire mul_div_op = (c_alu_mode == `ALU_MODE_MUL) || (c_alu_mode == `ALU_MODE_DIV) || (c_alu_mode == `ALU_MODE_MOD); + +// Submodules +rf rf( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), .i_rst(i_rst), .i_d(i_reg_data), .o_lout(reg_l_con), + .o_rout(reg_r_con), .i_lout_sel(c_l_reg_sel), .i_rout_sel(c_r_reg_sel), + .i_ie(i_reg_ie), .i_gie(1'b1), .o_l_high_out(reg_l_high_con), + .dbg_r0(dbg_r0), .dbg_sel(dbg_reg_sel), .dbg_reg(dbg_reg_out)); + +alu alu( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_l(alu_l_bus), .i_r(alu_r_bus), .o_out(alu_res), .i_mode(c_alu_mode), + .o_flags(alu_flags_d), .i_carry(alu_flags_q[`ALU_FLAG_C] & c_alu_carry_en)); + +alu_mul_div alu_mul_div ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), + .i_rst(i_rst), + + .i_a(alu_l_bus), .i_b(alu_r_bus), .o_d(alu_mul_res), + .i_submit(mul_div_op & i_submit), .i_flush(i_invalidate), + .o_busy(alu_mul_busy), + .i_mul(c_alu_mode == `ALU_MODE_MUL), + .i_div(c_alu_mode == `ALU_MODE_DIV), + .i_mod(c_alu_mode == `ALU_MODE_MOD) +); + +pc #(.INT_VEC(INT_VEC)) pc ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), .i_rst(i_rst), .i_bus(c_sreg_store | c_sreg_irt ? (c_sreg_irt ? sreg_out : sreg_in) : alu_bus), + .i_c_pc_inc((c_pc_inc | (~jump_dec_en & jump_dec_valid)) & exec_submit), .i_c_pc_ie((c_pc_ie | (jump_dec_en & jump_dec_valid) | pc_write) & exec_submit), + .o_pc(pc_val), .i_c_pc_irq(irq), .o_pc_ovf(pc_overflow)); + +// Cpu control registers +register #(.N(`ALU_FLAG_CNT)) alu_flag_reg ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), .i_rst(i_rst), .i_d((alu_flags_sreg_ie ? sreg_in[`ALU_FLAG_CNT-1:0] : alu_flags_d)), + .o_d(alu_flags_q), .i_ie((c_alu_flags_ie | alu_flags_sreg_ie) & exec_submit)); + +// JUMP DECODE +reg jump_dec_en; +wire jump_dec_valid = c_jump_cond_code[`JUMP_CODE_BIT_EN]; + +wire jump_mispredict = jump_dec_valid & (jump_dec_en ^ i_jmp_predict); +wire pc_write = (pc_sreg_ie & c_sreg_store) | c_sreg_irt; + +always @(posedge i_clk) begin + o_flush <= ((jump_mispredict | pc_write | flush_instr_mmu) & exec_submit) | irq | pc_high_updated; // invalidate itself and all previous stages at next cycle +end + +`define JUMP_CODE_UNCOND`JUMP_CODE_W'b10000 +`define JUMP_CODE_CARRY `JUMP_CODE_W'b10001 +`define JUMP_CODE_EQUAL `JUMP_CODE_W'b10010 +`define JUMP_CODE_LT `JUMP_CODE_W'b10011 +`define JUMP_CODE_GT `JUMP_CODE_W'b10100 +`define JUMP_CODE_LE `JUMP_CODE_W'b10101 +`define JUMP_CODE_GE `JUMP_CODE_W'b10110 +`define JUMP_CODE_NE `JUMP_CODE_W'b10111 +`define JUMP_CODE_OVF `JUMP_CODE_W'b11000 +`define JUMP_CODE_PAR `JUMP_CODE_W'b11001 +`define JUMP_CODE_GTU `JUMP_CODE_W'b11010 +`define JUMP_CODE_GEU `JUMP_CODE_W'b11011 +`define JUMP_CODE_LEU `JUMP_CODE_W'b11100 +always @(*) begin + case (c_jump_cond_code[`JUMP_CODE_W-1:0]) + `JUMP_CODE_UNCOND: + jump_dec_en = 1'b1; + `JUMP_CODE_CARRY: + jump_dec_en = alu_flags_q[`ALU_FLAG_C]; + `JUMP_CODE_EQUAL: + jump_dec_en = alu_flags_q[`ALU_FLAG_Z]; + `JUMP_CODE_LT: + jump_dec_en = alu_flags_q[`ALU_FLAG_N]; + `JUMP_CODE_GT: + jump_dec_en = ~(alu_flags_q[`ALU_FLAG_N] | alu_flags_q[`ALU_FLAG_Z]); + `JUMP_CODE_LE: + jump_dec_en = alu_flags_q[`ALU_FLAG_N] | alu_flags_q[`ALU_FLAG_Z]; + `JUMP_CODE_GE: + jump_dec_en = ~alu_flags_q[`ALU_FLAG_N]; + `JUMP_CODE_NE: + jump_dec_en = ~alu_flags_q[`ALU_FLAG_Z]; + `JUMP_CODE_OVF: + jump_dec_en = alu_flags_q[`ALU_FLAG_O]; + `JUMP_CODE_PAR: + jump_dec_en = alu_flags_q[`ALU_FLAG_P]; + `JUMP_CODE_GTU: + jump_dec_en = ~(alu_flags_q[`ALU_FLAG_C] | alu_flags_q[`ALU_FLAG_Z]); + `JUMP_CODE_GEU: + jump_dec_en = ~alu_flags_q[`ALU_FLAG_C]; + `JUMP_CODE_LEU: + jump_dec_en = alu_flags_q[`ALU_FLAG_C] | alu_flags_q[`ALU_FLAG_Z]; + default: + jump_dec_en = 1'b0; + endcase +end + +// Forwarding to next pipeline stage +always @(posedge i_clk) begin + if (i_rst) begin + o_submit <= 1'b0; + end else if (exec_submit) begin + o_addr <= alu_bus; + o_data <= (c_mem_access ? reg_r_con : + (c_sreg_load | c_sreg_jal_over ? sreg_out + (c_sreg_jal_over ? `RW'b1 : `RW'b0) + : alu_bus)); + o_reg_ie <= c_rf_ie; + o_mem_access <= c_mem_access; + o_mem_we <= c_mem_we; + o_mem_width <= c_mem_width; + o_mem_long_mode <= c_mem_long & sreg_long_ptr_en; + o_mem_addr_high <= computed_mem_addr_high; + o_submit <= 1'b1; + end else begin + o_submit <= 1'b0; + end +end + +reg prev_sys; // Execute sys instruction and trigger interrupt at next cycle to resume from next instruction +always @(posedge i_clk) begin + if (i_rst) begin + prev_sys <= 1'b0; + end else if (c_sys & exec_submit) begin + prev_sys <= 1'b1; + end else begin + prev_sys <= 1'b0; + end +end + +reg trap_exception; +always @(posedge i_clk) begin + if (i_rst) begin + trap_exception <= 1'b0; + end else begin + trap_exception <= trap_flag & exec_submit; + end +end + +reg [`RW-1:0] mem_stage_pc; +always @(posedge i_clk) begin + if (i_rst) + mem_stage_pc <= `RW'b0; + else if (exec_submit) + mem_stage_pc <= pc_val; +end + +// Special registers +`define SREG_PC `RW'b0 +`define SREG_PRIV_CTRL `RW'b1 +`define SREG_JTR `RW'b10 +`define SREG_IRQ_PC `RW'b11 +`define SREG_ALU_FLAGS `RW'b100 +`define SREG_IRQ_FLAGS `RW'b101 +`define SREG_SCRATCH `RW'b110 +`define SREG_CPUID `RW'b111 +`define SREG_COREID `RW'b1000 +`define SREG_MT_IRQ `RW'b1001 //,1010, 1011 +`define SREG_PC_HIGH `RW'b1100 +`define SREG_PC_HIGH_BUFF `RW'b1101 + +reg pc_sreg_ie, sreg_priv_control_ie, sreg_irq_pc_ie, alu_flags_sreg_ie, sreg_jtr_ie, sreg_scratch_ie, sreg_pc_high_ie, sreg_pc_high_buff_ie; +wire [`RW-1:0] sreg_priv_control_out, sreg_irq_pc_out, sreg_scratch_out; +always @* begin + {pc_sreg_ie, sreg_irq_pc_ie, sreg_priv_control_ie, alu_flags_sreg_ie, sreg_jtr_ie, sreg_scratch_ie, sreg_pc_high_ie, sreg_pc_high_buff_ie} = 8'b0; + case (i_imm) + `SREG_PC: begin + sreg_out = pc_val; + pc_sreg_ie = c_sreg_store; + end + `SREG_PRIV_CTRL: begin + sreg_out = sreg_priv_control_out; + sreg_priv_control_ie = c_sreg_store; + end + `SREG_IRQ_PC: begin + sreg_out = sreg_irq_pc_out; + sreg_irq_pc_ie = c_sreg_store; + end + `SREG_JTR: begin + sreg_out = {13'b0, sreg_jtr_out}; + sreg_jtr_ie = c_sreg_store & sreg_priv_mode; + end + `SREG_ALU_FLAGS: begin + sreg_out = {11'b0, alu_flags_q}; + alu_flags_sreg_ie = c_sreg_store; + end + `SREG_IRQ_FLAGS: begin + sreg_out = {11'b0, sreg_irq_flags_out}; + end + `SREG_SCRATCH: begin + sreg_out = sreg_scratch_out; + sreg_scratch_ie = c_sreg_store; + end + `SREG_CPUID: begin + sreg_out = 16'b1110_0000_0001_0001; + end + `SREG_COREID: begin + sreg_out = CORENO; + end + `SREG_MT_IRQ: begin // write is handled in upper_core + sreg_out = i_core_int_sreg; + end + `SREG_PC_HIGH: begin + sreg_pc_high_ie = c_sreg_store; + sreg_out = {8'b0, pc_high_out}; + end + `SREG_PC_HIGH_BUFF: begin + sreg_pc_high_buff_ie = c_sreg_store; + sreg_out = {8'b0, pc_high_buff_out}; + end + default: + sreg_out = 16'b0; + endcase + + if(c_sreg_jal_over) + sreg_out = pc_val; + if(c_sreg_irt) + sreg_out = sreg_irq_pc_out; +end + +// Special registers control + + +wire [`RW-1:0] priv_in = (irq ? (`RW'b0001) : (c_sreg_irt ? (sreg_priv_control_out | `RW'h0004) : sreg_in)); // disable irq and paging flag on interrupt and re-enable on return +register #(.RESET_VAL(`RW'b0001)) sreg_priv_control ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), .i_rst(i_rst), .i_d(priv_in), .o_d(sreg_priv_control_out), + .i_ie((((sreg_priv_control_ie & sreg_priv_mode) | c_sreg_irt) & exec_submit) | irq)); + +wire sreg_priv_mode = sreg_priv_control_out[0]; +wire sreg_data_page = sreg_priv_control_out[1]; +wire irq_en = sreg_priv_control_out[2]; +wire sreg_long_ptr_en = sreg_priv_control_out[3]; + +register sreg_irq_pc ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), .i_rst(i_rst), .i_d(sreg_irq_pc_ie ? sreg_in : (i_mem_exception ? mem_stage_pc : pc_val)), .o_d(sreg_irq_pc_out), .i_ie(irq | (sreg_irq_pc_ie & exec_submit))); + +wire [2:0] sreg_jtr_buff_o, sreg_jtr_out; +wire jtr_jump_en = (pc_sreg_ie | jump_dec_valid | c_sreg_irt) & exec_submit; +wire jtr_irqh_write = irq; +wire [2:0] jtr_buff_in = (irq ? 3'b000 : sreg_in[2:0]); +wire [2:0] jtr_in = (irq ? 3'b000 : sreg_jtr_buff_o); +register #(.RESET_VAL((CORENO == 0 ? 3'b001 : 3'b000)), .N(3)) sreg_jtr_buff ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), .i_rst(i_rst), .i_d(jtr_buff_in), .o_d(sreg_jtr_buff_o), .i_ie(sreg_jtr_ie | jtr_irqh_write)); +register #(.RESET_VAL((CORENO == 0 ? 3'b001 : 3'b000)), .N(3)) sreg_jtr ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), .i_rst(i_rst), .i_d(jtr_in), .o_d(sreg_jtr_out), .i_ie(jtr_jump_en | jtr_irqh_write)); +assign o_c_instr_page = sreg_jtr_out[0]; +wire trap_flag = sreg_jtr_out[1]; +wire long_pc_mode = sreg_jtr_out[2]; + +register sreg_scratch ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), .i_rst(i_rst), .i_d(sreg_in), .o_d(sreg_scratch_out), .i_ie(sreg_scratch_ie & exec_submit)); + +wire [4:0] sreg_irq_flags_in = {(i_core_int & irq_en), i_mem_exception, trap_exception, prev_sys, (i_irq & irq_en)}; +wire [4:0] sreg_irq_flags_out; +register #(.N(5)) sreg_irq_flags ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), .i_rst(i_rst), .i_d(sreg_irq_flags_in), .o_d(sreg_irq_flags_out), .i_ie(irq)); + +wire immu_write = c_sreg_store & exec_submit & (sr_bus_addr >= `RW'h100 && sr_bus_addr < `RW'h100 + 16); // flush after write to mmu is executed +wire flush_instr_mmu = (immu_write & o_c_instr_page) | ((jtr_in[0] ^ sreg_jtr_out[0]) & (jtr_jump_en | jtr_irqh_write)) | pc_high_updated; +always @(posedge i_clk) + o_icache_flush <= flush_instr_mmu & ~i_rst; + +// Delays disable of data paging in case of interrupt. MEMWB stage is still executing and changing +// address during request would break commited result. In other cases special handling is not needed, +// becaue sregs are updated only when next stage is ready +always @(posedge i_clk) begin + if (i_rst) + o_c_data_page <= 1'b0; + else if (i_next_ready) + o_c_data_page <= sreg_data_page; +end + +// Higher part of PC in long pointer mode +// long pc mode is disabled on interrupt and reabled via jtr at irt, bot registers can be recovered +wire [7:0] pc_high = (long_pc_mode ? pc_high_out : 'b0); + +wire pc_high_ovf = pc_overflow & long_pc_mode & exec_submit; +wire pc_high_jtr = (pc_sreg_ie | jump_dec_valid | c_sreg_irt) & long_pc_mode & exec_submit; +// Update from buffer at jump or at pc overflow +wire [7:0] pc_high_in = (sreg_pc_high_ie ? sreg_in[7:0] : (pc_high_jtr ? pc_high_buff_out : pc_high_out+7'b1)); +wire [7:0] pc_high_out; +register #(.N(8), .RESET_VAL(8'h80)) pc_high_reg ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), .i_rst(i_rst), + .i_d(pc_high_in), + .o_d(pc_high_out), + .i_ie((pc_high_ovf | pc_high_jtr | sreg_pc_high_ie) & exec_submit) +); + +// Update buffer on function call (JAL) with current pointer +wire [7:0] pc_high_buff_in = (sreg_pc_high_buff_ie ? sreg_in[7:0] : pc_high); +wire [7:0] pc_high_buff_out; +register #(.N(8), .RESET_VAL(8'h80)) pc_high_buff_reg ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), .i_rst(i_rst), + .i_d(pc_high_buff_in), + .o_d(pc_high_buff_out), + .i_ie((sreg_pc_high_buff_ie | (c_sreg_jal_over & long_pc_mode)) & exec_submit) +); + +wire pc_high_updated = |(prev_pc_high ^ pc_high); // flush pipeline and cache on update. current instuction is incorrect +reg [7:0] prev_pc_high; +always @(posedge i_clk) begin + if (i_rst) prev_pc_high <= 'b0; + else prev_pc_high <= pc_high; +end + +assign o_c_instr_long_mode = long_pc_mode; +assign o_instr_addr_high = pc_high; + +wire [7:0] computed_mem_addr_high = (~alu_flags_d[`ALU_FLAG_C] & alu_r_bus[`RW-1] ? reg_l_high_con[7:0]-8'b1 : + (alu_flags_d[`ALU_FLAG_C] ? reg_l_high_con[7:0]+8'b1 : reg_l_high_con[7:0])); + +assign sr_bus_addr = i_imm; +assign sr_bus_we = c_sreg_store & sreg_priv_mode & exec_submit; +assign sr_bus_data_o = sreg_in; + +assign dbg_out = {o_ready, pc_val, dbg_reg_out}; + +endmodule
diff --git a/verilog/rtl/ppcpu/rtl/core/fetch.v b/verilog/rtl/ppcpu/rtl/core/fetch.v new file mode 100644 index 0000000..9e7dcfb --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/core/fetch.v
@@ -0,0 +1,150 @@ +`include "config.v" + +// Instruction fetch stage v2 + +module fetch ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input i_clk, + input i_rst, + + output [`RW-1:0] mem_addr, // address must be valid only if submit is set (registered on other end) + input [`I_SIZE-1:0] mem_data, + input mem_ack, + output mem_submit, // pipelined submit signal + + input i_next_ready, + output reg o_submit, + input i_flush, + + output reg [`I_SIZE-1:0] o_instr, + output reg o_jmp_predict, + + input [`RW-1:0] i_exec_pc, + + output dbg_out +); + +assign mem_addr = (pc_reset_override ? `RW'b0 : ((i_flush | pc_flush_override) ? i_exec_pc : pred_pc)); +assign mem_submit = (((mem_ack & ~out_buffer_valid & i_next_ready & ~disable_prediction) | pc_reset_override | (pc_flush_override & ~instr_wait) | (out_buffer_valid & out_buff_read & ~disable_prediction))) & ~i_rst; + +reg pc_reset_override; +always @(posedge i_clk) begin + if (i_rst) begin + pc_reset_override <= 1'b1; + end else if (mem_submit) begin + pc_reset_override <= 1'b0; + end +end + +reg pc_flush_override; +always @(posedge i_clk) begin + if (i_rst | mem_submit) + pc_flush_override <= 1'b0; + else if (i_flush) + pc_flush_override <= 1'b1; +end + +reg flush_event_invalidate; +always @(posedge i_clk) begin + if (i_rst | mem_ack) + flush_event_invalidate <= 1'b0; + else if (i_flush & instr_wait & ~mem_ack) + flush_event_invalidate <= 1'b1; +end + +reg instr_wait; +always @(posedge i_clk) begin + if (i_rst) + instr_wait <= 1'b0; + else if (mem_submit) + instr_wait <= 1'b1; + else if (mem_ack) + instr_wait <= 1'b0; +end + +wire out_ready = out_buffer_valid | mem_ack; +wire [`I_SIZE-1:0] out_instr = (out_buffer_valid ? out_buffer_data_instr : mem_data); +wire out_jmp_predict = (out_buffer_valid ? out_buffer_data_pred : current_req_branch_pred); +wire submitable = out_ready & i_next_ready & ~(i_flush | flush_event_invalidate); +always @(posedge i_clk) begin + o_submit <= submitable; + if (submitable) begin + o_instr <= out_instr; + o_jmp_predict <= out_jmp_predict; + end +end + +reg [`RW-1:0] prev_request_pc; +always @(posedge i_clk) begin + if(mem_submit) + prev_request_pc <= mem_addr; +end + +wire current_req_branch_pred = (mem_ack ? branch_pred_res : prev_req_branch_pred); +reg prev_req_branch_pred; +always @(posedge i_clk) begin + if(mem_ack) + prev_req_branch_pred <= branch_pred_res; +end + +wire [`RW-1:0] pred_pc = (branch_pred_res ? branch_pred_imm : prev_request_pc + `RW'b1); + +wire [`I_SIZE-1:0] branch_pred_instr = (out_buffer_valid ? out_buffer_data_instr : mem_data); +wire [`RW-1:0] branch_pred_imm = branch_pred_instr[`I_SIZE-1:`I_SIZE-`RW]; + +// disable predicting after sys, irt, srs0 instructions (always ends with flush) +wire disable_prediction = (branch_pred_instr[6:0] == 7'h12) || (branch_pred_instr[6:0] == 7'h1e) || (branch_pred_instr[6:0] == 7'h11 && ~(|branch_pred_imm)); +reg branch_pred_res; + +// BRANCH PREDICTION / PC DECODE +always @(*) begin + if (pc_reset_override) begin + branch_pred_res = 1'b0; + end else if (branch_pred_instr[6:0] == 7'h0e) begin + if (branch_pred_instr[10:7] == 4'h0) begin + // unconditional jump + branch_pred_res = 1'b1; + end else begin + // try to predict jump + if (prev_request_pc > branch_pred_imm) begin + // back jump (taken) + branch_pred_res = 1'b1; + end else begin + // forward jump (not taken) + branch_pred_res = 1'b0; + end + end + end else if (branch_pred_instr[6:0] == 7'h0f) begin + branch_pred_res = 1'b1; + end else begin + branch_pred_res = 1'b0; + end +end + +reg [`I_SIZE-1:0] out_buffer_data_instr; +reg out_buffer_data_pred; +reg out_buffer_valid; + +wire out_buff_write = mem_ack & ~i_next_ready & ~(i_flush | flush_event_invalidate); +wire out_buff_read = out_buffer_valid & i_next_ready; +always @(posedge i_clk) begin + if (i_rst) begin + out_buffer_valid <= 1'b0; + end else if (i_flush) begin + out_buffer_valid <= 1'b0; + end else if (out_buff_write) begin + out_buffer_data_instr <= mem_data; + out_buffer_data_pred <= current_req_branch_pred; + out_buffer_valid <= 1'b1; + end else if (out_buffer_valid & i_next_ready) begin + out_buffer_valid <= 1'b0; + end +end + +assign dbg_out = instr_wait; + +endmodule
diff --git a/verilog/rtl/ppcpu/rtl/core/memwb.v b/verilog/rtl/ppcpu/rtl/core/memwb.v new file mode 100644 index 0000000..e0a5423 --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/core/memwb.v
@@ -0,0 +1,66 @@ +// Memory and Writeback stage +`include "config.v" + +module memwb ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input i_clk, + input i_rst, + + input [`RW-1:0] i_data, + input [`RW-1:0] i_addr, + input [7:0] i_addr_high, + input [`REGNO-1:0] i_reg_ie, + input i_mem_access, + input i_mem_long, + input i_mem_we, + input i_mem_width, + + output [`REGNO-1:0] o_reg_ie, + output [`RW-1:0] o_reg_data, + + input i_submit, + output o_ready, + + output reg o_mem_req, + output [`RW-1:0] o_mem_data, + output [`RW-1:0] o_mem_addr, + output [7:0] o_mem_addr_high, + output o_mem_long, + output o_mem_we, + input i_mem_ack, + input [`RW-1:0] i_mem_data, + output [`ADDR_BYTES-1:0] o_mem_sel, + input o_mem_exception +); + +wire [`RW-1:0] mem_result = (i_mem_width ? (i_addr[0] ? (i_mem_data>>`RW'h8) : (i_mem_data&`RW'hff)) : i_mem_data); +assign o_reg_data = (i_mem_access ? mem_result : i_data); + +assign o_mem_data = ((i_mem_width & i_addr[0]) ? (i_data<<`RW'h8) : i_data); +assign o_mem_addr = {(i_addr_high[0]&i_mem_long), 15'b0} | (i_addr>>`RW'b1); // LSB is used for byte selection in STDMEM +assign o_mem_we = i_mem_we; +assign o_mem_addr_high = (i_addr_high>>`RW'b1); +assign o_mem_long = i_mem_long; + +assign o_mem_sel = (i_mem_width ? (`ADDR_BYTES'b1 << i_addr[0]) : `ADDR_BYTES'b11); + +wire reg_ie = ((i_mem_access & i_mem_ack) | (~i_mem_access & i_submit)) & (|i_reg_ie); +assign o_reg_ie = (reg_ie ? i_reg_ie : `REGNO'b0); + +assign o_ready = (~o_mem_req | (o_mem_req & i_mem_ack)) & ~(i_submit & i_mem_access); + +always @(posedge i_clk) begin + if (i_rst) begin + o_mem_req <= 1'b0; + end else if (i_submit) begin + o_mem_req <= i_mem_access; + end else if (i_mem_ack | o_mem_exception) begin + o_mem_req <= 1'b0; + end +end + +endmodule
diff --git a/verilog/rtl/ppcpu/rtl/core/pc.v b/verilog/rtl/ppcpu/rtl/core/pc.v new file mode 100644 index 0000000..721e032 --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/core/pc.v
@@ -0,0 +1,35 @@ +`include "config.v" +module pc #(parameter INT_VEC = `RW'b1) ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input i_clk, + input i_rst, + + output reg [`RW-1:0] o_pc, + input [`RW-1:0] i_bus, + + input i_c_pc_inc, + input i_c_pc_ie, + input i_c_pc_irq, + + output o_pc_ovf +); + +always @(posedge i_clk) begin + if (i_rst) begin + o_pc <= `RW'b0; + end else if (i_c_pc_irq) begin + o_pc <= INT_VEC; + end else if (i_c_pc_ie) begin + o_pc <= i_bus; + end else if (i_c_pc_inc) begin + o_pc <= o_pc + `RW'b1; + end +end + +assign o_pc_ovf = o_pc[14] & i_c_pc_inc & ~i_c_pc_irq & ~i_c_pc_ie; // due to address shifiting, pc overflows at 14th bit + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/core/ports b/verilog/rtl/ppcpu/rtl/core/ports new file mode 100644 index 0000000..5e8fe50 --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/core/ports
@@ -0,0 +1,31 @@ +.i_clk(i_clk), +.i_rst(i_rst), +.i_disable(i_disable), +.o_req_addr(o_req_addr), +.o_req_active(o_req_active), +.o_req_ppl_submit(o_req_ppl_submit), +.i_req_data(i_req_data), +.i_req_data_valid(i_req_data_valid), +.dbg_r0(dbg_r0), +.dbg_pc(dbg_pc), +.o_mem_addr(o_mem_addr), +.o_mem_data(o_mem_data), +.i_mem_data(i_mem_data), +.o_mem_req(o_mem_req), +.o_mem_we(o_mem_we), +.i_mem_ack(i_mem_ack), +.o_mem_sel(o_mem_sel), +.o_mem_long(o_mem_long), +.o_mem_addr_high(o_mem_addr_high), +.i_irq(i_irq), +.o_c_instr_page(o_c_instr_page), +.o_c_data_page(o_c_data_page), +.sr_bus_addr(sr_bus_addr), +.sr_bus_data_o(sr_bus_data_o), +.sr_bus_we(sr_bus_we), +.o_icache_flush(o_icache_flush), +.i_mem_exception(i_mem_exception), +.i_mc_core_int(i_mc_core_int), +.i_core_int_sreg(i_core_int_sreg), +.o_c_instr_long(o_c_instr_long), +.o_instr_long_addr(o_instr_long_addr) \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/core/rf.v b/verilog/rtl/ppcpu/rtl/core/rf.v new file mode 100644 index 0000000..172d18e --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/core/rf.v
@@ -0,0 +1,75 @@ +`include "config.v" + +module rf ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input i_clk, + input i_rst, + + input i_gie, + input [`REGNO-1:0] i_ie, + input [`REGNO_LOG-1:0] i_lout_sel, + input [`REGNO_LOG-1:0] i_rout_sel, + + input [`RW-1:0] i_d, + output [`RW-1:0] o_lout, + output [`RW-1:0] o_rout, + output [`RW-1:0] o_l_high_out, + + output [`RW-1:0] dbg_r0, + input [`REGNO_LOG-1:0] dbg_sel, + output [`RW-1:0] dbg_reg +); + +wire [`RW-1:0] reg_outputs [`REGNO-1:0]; + +assign o_lout = reg_outputs[i_lout_sel]; +assign o_rout = reg_outputs[i_rout_sel]; +assign o_l_high_out = reg_outputs[(i_lout_sel&3'b110) | 3'b1]; + +assign dbg_r0 = reg_outputs[0]; +assign dbg_reg = reg_outputs[dbg_sel]; + +genvar i; +generate + for (i=0; i<`REGNO; i=i+1) begin : rf_regs + register rf_reg( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), + .i_rst(i_rst), + .i_d(i_d), + .i_ie(i_ie[i] & i_gie), + .o_d(reg_outputs[i]) + ); + end +endgenerate + +endmodule + +module register #(parameter N = `RW, parameter RESET_VAL = 0) ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + input i_clk, + input i_rst, + + input [N-1:0] i_d, + output reg [N-1:0] o_d, + input i_ie +); + +always @(posedge i_clk) begin + if (i_rst) + o_d <= RESET_VAL; + else if (i_ie) + o_d <= i_d; +end + + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/dcache/dcache.v b/verilog/rtl/ppcpu/rtl/dcache/dcache.v new file mode 100644 index 0000000..e6e408f --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/dcache/dcache.v
@@ -0,0 +1,301 @@ +`include "config.v" + +module dcache ( +`ifdef USE_POWER_PINS + inout vccd1, // User area 1 1.8V supply + inout vssd1, // User area 1 digital ground +`endif + + input i_clk, + input i_rst, + + input mem_req, + input mem_we, + output mem_ack, + input [`WB_ADDR_W-1:0] mem_addr, + input [`RW-1:0] mem_i_data, + output reg [`RW-1:0] mem_o_data, + input [1:0] mem_sel, + input mem_cache_enable, + output mem_exception, + + // output interface + output reg wb_cyc, + output reg wb_stb, + input [`RW-1:0] wb_i_dat, + output reg [`RW-1:0] wb_o_dat, + output reg [`WB_ADDR_W-1:0] wb_adr, + output reg wb_we, + output [1:0] wb_sel, + output wb_4_burst, + input wb_ack, + input wb_err + + // TODO: Multicore MSI cache protocol +); + +// VIRTUALLY INDEXED, no need to flush on context switch, coherent with dma by cache protocols +`define TAG_SIZE 16 +`define LINE_SIZE 64 +// 16b tag + 64b line + 2b valid dirty +`define ENTRY_SIZE 82 +`define CACHE_ASSOC 2 +`define CACHE_ASSOC_W 1 +`define CACHE_ENTR_N 64 +`define CACHE_OFF_W 2 + +`define CACHE_IDX_WIDTH 6 +`define CACHE_IDXES 64 + +`define VALID_BIT 0 +`define DIRTY_BIT 1 + +`define SW 3 +`define S_IDLE `SW'b0 +`define S_CREAD `SW'b1 +`define S_MISS_RD `SW'b10 +`define S_MISS_WR `SW'b11 +`define S_RQ_WR `SW'b100 +`define S_RQ_WR_WAIT `SW'b101 +`define S_NOCACHE `SW'b110 +reg [`SW-1:0] state; + +wire [`ENTRY_SIZE-1:0] cache_mem_in; +wire [`ENTRY_SIZE-1:0] cache_out [`CACHE_ASSOC-1:0]; +reg [`CACHE_ASSOC-1:0] cache_we; +wire [`CACHE_ASSOC-1:0] cache_hit; + +wire [`CACHE_IDX_WIDTH-1:0] cache_idx = mem_addr[7:2]; +wire [`CACHE_OFF_W-1:0] cache_offset = mem_addr[1:0]; +wire [`TAG_SIZE-1:0] cache_compare_tag = mem_addr[`WB_ADDR_W-1:8]; + +`ifndef USE_OC_RAM + +dcache_ram mem_set_0 ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), .i_rst(i_rst), .i_addr(cache_idx), .i_data(cache_mem_in), + .o_data(cache_out[0]), .i_we(cache_we[0])); +dcache_ram mem_set_1 ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), .i_rst(i_rst), .i_addr(cache_idx), .i_data(cache_mem_in), + .o_data(cache_out[1]), .i_we(cache_we[1])); + +`else + +ocram_dcache mem_set_0 ( + .clock(i_clk), .address(cache_idx), .data(cache_mem_in), + .q(cache_out[0]), .wren(cache_we[0])); +ocram_dcache mem_set_1 ( + .clock(i_clk), .address(cache_idx), .data(cache_mem_in), + .q(cache_out[1]), .wren(cache_we[1])); + +`endif + +assign cache_hit[0] = (cache_out[0][`ENTRY_SIZE-1:`ENTRY_SIZE-`TAG_SIZE] == cache_compare_tag) && cache_out[0][`VALID_BIT]; +assign cache_hit[1] = (cache_out[1][`ENTRY_SIZE-1:`ENTRY_SIZE-`TAG_SIZE] == cache_compare_tag) && cache_out[1][`VALID_BIT]; + +wire cache_ghit = (state == `S_CREAD) && (|cache_hit); +wire cache_gmiss = (state == `S_CREAD) && ~(|cache_hit); + +wire mem_op_end = wb_cyc & wb_stb & (wb_ack | wb_err) & (&line_burst_cnt); +wire mem_fetch_end = (state == `S_MISS_RD) && mem_op_end; +wire write_to_cache = (state == `S_RQ_WR); + +assign mem_ack = (state == `S_CREAD && ~mem_we && cache_ghit) | (mem_fetch_end && ~mem_we) | (state == `S_RQ_WR) | (state == `S_NOCACHE && wb_cyc && wb_stb && (wb_ack | wb_err)); +// TODO: Emit exception on bus error and retry bus instruction +//assign mem_bus_err_flush = (state == `S_NOCACHE && wb_cyc && wb_stb && wb_err) | (mem_fetch_end && ~mem_we && transfer_bus_err_w) | (state == `S_RQ_WR && transfer_bus_err_w); + +always @(posedge i_clk) begin + if (i_rst) begin + state <= `S_IDLE; + end else if (state == `S_IDLE && mem_req && ~mem_cache_enable && ~illegal_address) begin + state <= `S_NOCACHE; + end else if (state == `S_IDLE && mem_req && ~illegal_address) begin + state <= `S_CREAD; + end else if (state == `S_CREAD && cache_ghit && ~mem_we) begin + state <= `S_IDLE; + end else if (state == `S_CREAD && cache_ghit && mem_we) begin + state <= `S_RQ_WR; + end else if (state == `S_CREAD && cache_gmiss && ~all_entries_dirty) begin + state <= `S_MISS_RD; + end else if (state == `S_CREAD && cache_gmiss && all_entries_dirty) begin + state <= `S_MISS_WR; + end else if (state == `S_MISS_RD && mem_fetch_end && ~mem_we) begin + state <= `S_IDLE; + end else if (state == `S_MISS_RD && mem_fetch_end && mem_we) begin + state <= `S_RQ_WR_WAIT; + end else if (state == `S_RQ_WR_WAIT) begin + state <= `S_RQ_WR; + end else if (state == `S_MISS_WR && mem_op_end) begin + state <= `S_MISS_RD; + end else if (state == `S_RQ_WR) begin + state <= `S_IDLE; + end else if (state == `S_NOCACHE && wb_cyc && wb_stb && (wb_ack | wb_err)) begin + state <= `S_IDLE; + end +end + +`define FIRST_PAGE `WB_ADDR_W'h000800 +wire illegal_address = mem_addr < `FIRST_PAGE; + +assign mem_exception = (state == `S_IDLE && mem_req && illegal_address) | (state == `S_NOCACHE && wb_cyc && wb_stb && wb_err); + +wire wb_sel_adr_source = (state == `S_MISS_WR) | (state == `S_CREAD && cache_gmiss && all_entries_dirty); + +wire [23:0] wb_adr_w = (wb_sel_adr_source ? {old_entry_addr[`WB_ADDR_W-1:2], line_burst_cnt} : {mem_addr[`WB_ADDR_W-1:2], line_burst_cnt}); +assign wb_sel = (mem_cache_enable ? 2'b11 : mem_sel); +assign wb_4_burst = mem_cache_enable; + +wire cache_we_en = (mem_fetch_end & ~transfer_bus_err_w) | write_to_cache; +assign cache_mem_in = (write_to_cache ? cache_update_entry : {cache_compare_tag, pre_assembled_line, 2'b01}); + +wire all_entries_dirty = (&cache_out[0][`DIRTY_BIT:`VALID_BIT]) & (&cache_out[1][`DIRTY_BIT:`VALID_BIT]); +wire [`WB_ADDR_W-1:0] old_entry_addr = {write_source_entry[`ENTRY_SIZE-1:`ENTRY_SIZE-`TAG_SIZE], mem_addr[`CACHE_OFF_W+`CACHE_IDX_WIDTH-1:0]}; + +reg [`LINE_SIZE-1:0] line_collect; +wire [`LINE_SIZE-1:0] pre_assembled_line = {wb_i_dat, line_collect[47:0]}; + +reg transfer_bus_err; +wire transfer_bus_err_w = transfer_bus_err | (wb_cyc & wb_stb & wb_err); + +reg int_wb_cyc; + +reg [`CACHE_OFF_W-1:0] line_burst_cnt; +always @(posedge i_clk) begin + if (i_rst) begin + int_wb_cyc <= 1'b0; + transfer_bus_err <= 1'b0; + end else if (state == `S_IDLE && mem_req && ~mem_cache_enable && ~illegal_address) begin + int_wb_cyc <= 1'b1; + wb_we <= mem_we; + line_burst_cnt <= mem_addr[1:0]; + transfer_bus_err <= 1'b0; + end else if (cache_gmiss || (state == `S_MISS_WR && mem_op_end)) begin + line_burst_cnt <= 2'b0; + int_wb_cyc <= 1'b1; + wb_we <= all_entries_dirty & ~mem_op_end; + transfer_bus_err <= transfer_bus_err & ~cache_gmiss; // clear error only on new request. Don't update cache if first write failed + end else if (mem_op_end || (state == `S_NOCACHE && wb_cyc & wb_stb & (wb_ack | wb_err))) begin + line_burst_cnt <= 2'b0; + int_wb_cyc <= 1'b0; + end else if (int_wb_cyc & wb_stb & (wb_ack | wb_err)) begin + line_burst_cnt <= line_burst_cnt + 1'b1; + transfer_bus_err <= transfer_bus_err | wb_err; + end +end + +// dealy wishbone start to cut combinational paths on addr +always @(posedge i_clk) begin + wb_adr <= wb_adr_w; + if (mem_op_end || (state == `S_NOCACHE && wb_cyc & wb_stb & (wb_ack | wb_err))) begin + wb_cyc <= 1'b0; + wb_stb <= 1'b0; + end else begin + wb_cyc <= int_wb_cyc; + wb_stb <= int_wb_cyc; + end +end + +always @(posedge i_clk) begin + case (line_burst_cnt) + default: line_collect[15:0] <= wb_i_dat; + 2'b01: line_collect[31:16] <= wb_i_dat; + 2'b10: line_collect[47:32] <= wb_i_dat; + 2'b11: line_collect[63:48] <= wb_i_dat; + endcase +end + +reg [`ENTRY_SIZE-1:0] cache_hit_entry; +always @* begin + case (cache_hit) + default: cache_hit_entry = cache_out[0]; + 2'b10: cache_hit_entry = cache_out[1]; + endcase +end + +wire [`ENTRY_SIZE-1:0] entry_out = (mem_fetch_end ? {`TAG_SIZE'b0, pre_assembled_line, 2'b00} : cache_hit_entry); + +always @* begin + if (mem_cache_enable) begin + case (cache_offset) + default: mem_o_data = entry_out[17:2]; + 2'b01: mem_o_data = entry_out[33:18]; + 2'b10: mem_o_data = entry_out[49:34]; + 2'b11: mem_o_data = entry_out[65:50]; + endcase + end else begin + mem_o_data = wb_i_dat; + end +end + +wire [`ENTRY_SIZE-1:0] write_source_entry = cache_out[tx_cache_sel]; +always @* begin + if (mem_cache_enable) begin + case (line_burst_cnt) + default: wb_o_dat = write_source_entry[17:2]; + 2'b01: wb_o_dat = write_source_entry[33:18]; + 2'b10: wb_o_dat = write_source_entry[49:34]; + 2'b11: wb_o_dat = write_source_entry[65:50]; + endcase + end else begin + wb_o_dat = mem_i_data; + end +end + +reg [`ENTRY_SIZE-1:0] cache_update_entry; +always @* begin + case ({mem_addr[1:0], mem_sel}) + default: cache_update_entry = {write_source_entry[`ENTRY_SIZE-1:18], mem_i_data, 2'b11}; + 4'b0001: cache_update_entry = {write_source_entry[`ENTRY_SIZE-1:10], mem_i_data[7:0], 2'b11}; + 4'b0010: cache_update_entry = {write_source_entry[`ENTRY_SIZE-1:18], mem_i_data[15:8], write_source_entry[9:2], 2'b11}; + 4'b0111: cache_update_entry = {write_source_entry[`ENTRY_SIZE-1:34], mem_i_data, write_source_entry[17:2], 2'b11}; + 4'b0101: cache_update_entry = {write_source_entry[`ENTRY_SIZE-1:26], mem_i_data[7:0], write_source_entry[17:2], 2'b11}; + 4'b0110: cache_update_entry = {write_source_entry[`ENTRY_SIZE-1:34], mem_i_data[15:8], write_source_entry[25:2], 2'b11}; + 4'b1011: cache_update_entry = {write_source_entry[`ENTRY_SIZE-1:50], mem_i_data, write_source_entry[33:2], 2'b11}; + 4'b1001: cache_update_entry = {write_source_entry[`ENTRY_SIZE-1:42], mem_i_data[7:0], write_source_entry[33:2], 2'b11}; + 4'b1010: cache_update_entry = {write_source_entry[`ENTRY_SIZE-1:50], mem_i_data[15:8], write_source_entry[41:2], 2'b11}; + 4'b1111: cache_update_entry = {write_source_entry[`ENTRY_SIZE-1:66], mem_i_data, write_source_entry[49:2], 2'b11}; + 4'b1101: cache_update_entry = {write_source_entry[`ENTRY_SIZE-1:58], mem_i_data[7:0], write_source_entry[49:2], 2'b11}; + 4'b1110: cache_update_entry = {write_source_entry[`ENTRY_SIZE-1:66], mem_i_data[15:8], write_source_entry[57:2], 2'b11}; + endcase +end + +reg cache_sel; +reg tx_cache_sel, prev_cache_sel; +always @* begin + if (~cache_out[0][`VALID_BIT]) cache_sel = 1'b0; + else if (~cache_out[1][`VALID_BIT]) cache_sel = 1'b1; + else if (~cache_out[0][`DIRTY_BIT]) cache_sel = 1'b0; + else if (~cache_out[1][`DIRTY_BIT]) cache_sel = 1'b1; + else cache_sel = prev_cache_sel + 1'b1; +end + +always @(posedge i_clk) begin + if (state == `S_CREAD && cache_gmiss && ~i_rst) begin + tx_cache_sel <= cache_sel; + prev_cache_sel <= tx_cache_sel; + end else if (state == `S_CREAD && cache_ghit && mem_we && ~i_rst) begin + tx_cache_sel <= (|(cache_hit&2'b1) ? 1'b0 : 1'b1); + prev_cache_sel <= tx_cache_sel; + end +end + +always @* begin + cache_we[1:0] = 2'b0; + cache_we[tx_cache_sel] = cache_we_en; +end + +endmodule + +`undef SW +`undef TAG_SIZE +`undef LINE_SIZE +`undef ENTRY_SIZE +`undef CACHE_ASSOC +`undef CACHE_ENTR_N +`undef CACHE_IDX_WIDTH +`undef CACHE_IDXES \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/dcache/dcache_ram.v b/verilog/rtl/ppcpu/rtl/dcache/dcache_ram.v new file mode 100644 index 0000000..8df62f7 --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/dcache/dcache_ram.v
@@ -0,0 +1,30 @@ +module dcache_ram ( +`ifdef USE_POWER_PINS + inout vccd1, // User area 1 1.8V supply + inout vssd1, // User area 1 digital ground +`endif + + input i_clk, + input i_rst, + + input [5:0] i_addr, + input [81:0] i_data, + output reg [81:0] o_data, + input i_we +); + +reg [81:0] mem [63:0]; + +always @(posedge i_clk) begin + if (i_rst) begin + for (integer row = 0; row < 64; row = row+1) begin + mem[row][1:0] <= 2'b0; + end + end else begin + if(i_we) + mem[i_addr] <= i_data; + o_data <= mem[i_addr]; + end +end + +endmodule
diff --git a/verilog/rtl/ppcpu/rtl/embed/gpio.v b/verilog/rtl/ppcpu/rtl/embed/gpio.v new file mode 100644 index 0000000..ca6eca0 --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/embed/gpio.v
@@ -0,0 +1,53 @@ +`include "config.v" + +module gpio #(parameter N = 4) ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input i_clk, + input i_rst, + + input [23:0] wb_adr, + input wb_cyc, + input wb_stb, + output wb_ack, + input wb_we, + input [15:0] wb_i_dat, + output reg [15:0] wb_o_dat, + + output reg [N-1:0] gpio_out, + input [N-1:0] gpio_in, + output reg [N-1:0] gpio_dir +); + +localparam GPIO_READ = `WB_ADDR_W'h001010; +localparam GPIO_WRITE = `WB_ADDR_W'h001011; +localparam GPIO_DIR = `WB_ADDR_W'h001012; + +always @(posedge i_clk) begin + if (i_rst) begin + gpio_dir <= {N{1'b1}}; // default inputs + gpio_out <= {N{1'b0}}; + end else if (wb_cyc & wb_stb & wb_we & (wb_adr == GPIO_WRITE)) begin + gpio_out <= wb_i_dat[N-1:0]; + end else if (wb_cyc & wb_stb & wb_we & (wb_adr == GPIO_DIR)) begin + gpio_dir <= wb_i_dat[N-1:0]; + end +end + +always @* begin + wb_o_dat = 16'b0; + case (wb_adr) + default: + wb_o_dat[N-1:0] = gpio_in; + GPIO_WRITE: + wb_o_dat[N-1:0] = gpio_out; + GPIO_DIR: + wb_o_dat[N-1:0] = gpio_dir; + endcase +end +assign wb_ack = wb_cyc & wb_stb; + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/embed/int_ram.v b/verilog/rtl/ppcpu/rtl/embed/int_ram.v new file mode 100644 index 0000000..dfb614c --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/embed/int_ram.v
@@ -0,0 +1,29 @@ +`include "config.v" + +// Internal ram for use in embed mode (programmable from outside) +// It maps to address 0x800000 and 0x100000 noncacheable (the same segment, p/m split left to the user) +// In other case maps to 0x7ffe00 cacheable as internal fast memory + +module int_ram ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input i_clk, + + input [8:0] i_addr, // 1kB + input [`RW-1:0] i_data, + output reg [`RW-1:0] o_data, + input i_we +); + +reg [`RW-1:0] mem [511:0]; + +always @(posedge i_clk) begin + if(i_we) + mem[i_addr] <= i_data; + o_data <= mem[i_addr]; +end + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/embed/sspi.v b/verilog/rtl/ppcpu/rtl/embed/sspi.v new file mode 100644 index 0000000..a500803 --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/embed/sspi.v
@@ -0,0 +1,130 @@ +module sspi ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input i_clk, + input i_rst, + + input spi_clk, + input spi_mosi, + output reg spi_miso, + + output reg wb_cyc, + output reg wb_stb, + output reg [23:0] wb_adr, + input [15:0] wb_i_dat, + output reg [15:0] wb_o_dat, + output reg wb_we, + output [1:0] wb_sel, + input wb_ack, + input wb_err +); + +reg [2:0] sy_clk; +wire sclk_edge = (sy_clk[2] ^ sy_clk[1]) & ~sy_clk[2]; +always @(posedge i_clk) begin + if (i_rst) sy_clk <= 3'b0; + else sy_clk <= {sy_clk[1], sy_clk[0], spi_clk}; +end + +reg [23:0] req_addr; +reg [15:0] req_data; +reg [15:0] res_data; +reg resp_err; + +reg [4:0] bit_cnt; +reg [3:0] state; +localparam STATE_IDLE = 4'b0; +localparam STATE_ADDR = 4'b1; +localparam STATE_RW = 4'b10; +localparam STATE_WRITE_DAT = 4'b11; +localparam STATE_WRITE_WB = 4'b100; +localparam STATE_WB_RESP = 4'b101; +localparam STATE_READ_WB = 4'b110; +localparam STATE_READ_DAT = 4'b111; + +always @(posedge i_clk) begin + if (i_rst) begin + state <= STATE_IDLE; + bit_cnt <= 'b0; + spi_miso <= 1'b1; + wb_cyc <= 1'b0; + wb_stb <= 1'b0; + end else if (sclk_edge) begin + if (state == STATE_IDLE) begin + spi_miso <= 1'b1; + if (~spi_mosi) + state <= STATE_ADDR; + end else if (state == STATE_ADDR) begin + req_addr[bit_cnt] <= spi_mosi; + bit_cnt <= bit_cnt + 5'b1; + + if (bit_cnt == 5'd23) begin + state <= STATE_RW; + bit_cnt <= 5'b0; + end + end else if (state == STATE_RW) begin + state <= (spi_mosi ? STATE_WRITE_DAT : STATE_READ_WB); + end else if (state == STATE_WRITE_DAT) begin + req_data[bit_cnt[3:0]] <= spi_mosi; + bit_cnt <= bit_cnt + 5'b1; + + if (bit_cnt == 5'd15) begin + state <= STATE_WRITE_WB; + bit_cnt <= 5'b0; + end + end else if (state == STATE_WRITE_WB) begin + wb_cyc <= 1'b1; + wb_stb <= 1'b1; + wb_we <= 1'b1; + + wb_adr <= req_addr; + wb_o_dat <= req_data; + + if ((wb_ack | wb_err) & wb_cyc) begin + state <= STATE_WB_RESP; + bit_cnt <= 5'b0; + + resp_err <= wb_err; + wb_cyc <= 1'b0; + wb_stb <= 1'b0; + + spi_miso <= 1'b0; // end of wait + end + end else if (state == STATE_WB_RESP) begin + spi_miso <= resp_err; + state <= STATE_IDLE; + end else if (state == STATE_READ_WB) begin + wb_cyc <= 1'b1; + wb_stb <= 1'b1; + wb_we <= 1'b0; + wb_adr <= req_addr; + + if ((wb_ack | wb_err) & wb_cyc) begin + state <= STATE_READ_DAT; + bit_cnt <= 5'b0; + + res_data <= wb_i_dat; + resp_err <= wb_err; + wb_cyc <= 1'b0; + wb_stb <= 1'b0; + + spi_miso <= 1'b0; // end of wait + end + end else if (state == STATE_READ_DAT) begin + spi_miso <= res_data[bit_cnt[3:0]]; + bit_cnt <= bit_cnt + 5'b1; + + if (bit_cnt == 5'd15) begin + state <= STATE_WB_RESP; + bit_cnt <= 5'b0; + end + end + end +end + +assign wb_sel = 2'b11; + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/icache/icache.v b/verilog/rtl/ppcpu/rtl/icache/icache.v new file mode 100644 index 0000000..e86024d --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/icache/icache.v
@@ -0,0 +1,229 @@ +`include "config.v" + +module icache ( +`ifdef USE_POWER_PINS + inout vccd1, // User area 1 1.8V supply + inout vssd1, // User area 1 digital ground +`endif + + input i_clk, + input i_rst, + + input mem_req, + output mem_ack, + input [`RW-1:0] mem_addr, + output reg [`I_SIZE-1:0] mem_data, + input mem_ppl_submit, + input mem_cache_flush, + + // output interface + output reg wb_cyc, + output reg wb_stb, + input [`RW-1:0] wb_i_dat, + output [`RW-1:0] wb_adr, + output wb_we, + input wb_ack, + output [1:0] wb_sel, + input wb_err +); + +assign wb_sel = 2'b11; +assign wb_we = 1'b0; + +`define TAG_SIZE 9 +`define LINE_SIZE 128 +// 9b tag + 128b line + 1b valid +`define ENTRY_SIZE 138 +`define CACHE_ASSOC 1 +`define CACHE_ENTR_N 32 +`define CACHE_SETS_N 8 +`define CACHE_OFF_W 2 + +`define CACHE_IDX_WIDTH 5 +`define CACHE_IDXES 32 + + +wire [`TAG_SIZE-1:0] compare_tag = cache_read_addr[15:7]; +wire [`TAG_SIZE-1:0] write_tag = cache_write_addr[15:7]; +wire [`CACHE_IDX_WIDTH-1:0] mem_index = (submit_pending ? submit_pending_addr[6:2] : mem_addr[6:2]); +wire [`CACHE_IDX_WIDTH-1:0] compare_index = cache_read_addr[6:2]; +wire [`CACHE_IDX_WIDTH-1:0] wire_index = cache_write_addr[6:2]; +wire [`CACHE_OFF_W-1:0] compare_off = cache_read_addr[1:0]; +wire [`CACHE_OFF_W-1:0] write_off = cache_write_addr[1:0]; + +wire [`ENTRY_SIZE-1:0] cache_mem_in = cache_write_entry; +wire [`ENTRY_SIZE-1:0] cache_out [`CACHE_ASSOC-1:0]; +reg [`CACHE_ASSOC-1:0] cache_we; +wire [`CACHE_ASSOC-1:0] cache_hit; + +reg [`RW-1:0] cache_read_addr, cache_write_addr; +reg cache_read_valid; +wire cache_write_valid = wb_cyc & wb_stb; +reg prev_write_compl; + +wire [`CACHE_IDX_WIDTH-1:0] cache_addr = ((|cache_we) ? wire_index : mem_index); + +`ifndef USE_OC_RAM + +icache_ram mem ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(i_clk), .i_addr(cache_addr), .i_data(cache_mem_in), + .o_data(cache_out[0]), .i_we(cache_we[0]) +); + +`else + +ocram_icache mem ( + .clock(i_clk), .address(cache_addr), .data(cache_mem_in), + .q(cache_out[0]), .wren(cache_we[0]) +); + +`endif + +// we have to use valid bits from delayed index for data to arrive at the same time as memory +assign cache_hit[0] = (cache_out[0][`ENTRY_SIZE-1:`ENTRY_SIZE-`TAG_SIZE] == compare_tag) && valid_bits[compare_index]; + +reg [`CACHE_ENTR_N-1:0] valid_bits; +always @(posedge i_clk) begin + if (i_rst | mem_cache_flush) begin + valid_bits <= `CACHE_ENTR_N'b0; + end else if (cache_we[0]) begin + valid_bits[cache_addr] <= cache_mem_in[0]; + end +end + +assign mem_ack = cache_ghit | mem_fetch_end; + +wire cache_miss = cache_read_valid & (~(|cache_hit) | flush_prev_cycle); +wire cache_ghit = cache_read_valid & ((|cache_hit) & ~flush_prev_cycle); + +always @(posedge i_clk) + cache_read_addr <= (submit_pending ? submit_pending_addr : mem_addr); + +always @(posedge i_clk) begin + if(cache_read_valid) + cache_write_addr <= cache_read_addr; +end + +always @(posedge i_clk) + prev_write_compl <= |cache_we; + +reg submit_pending; +reg [`RW-1:0] submit_pending_addr; +always @(posedge i_clk) begin + if(i_rst) + submit_pending <= 1'b0; + else if (mem_ppl_submit & ~accept_ok) begin + submit_pending <= 1'b1; + submit_pending_addr <= mem_addr; + end else if (accept_ok) + submit_pending <= 1'b0; +end + +wire accept_ok = mem_req & ~cache_write_valid & ~cache_miss; + +reg flush_prev_cycle; +always @(posedge i_clk) + flush_prev_cycle <= mem_cache_flush; // feedback after invalidating memortm takes 2 cycles; use this to force miss signal after 1 cycle + +always @(posedge i_clk) begin + if(i_rst) + cache_read_valid <= 1'b0; + else + cache_read_valid <= accept_ok & (submit_pending | mem_ppl_submit); +end + +wire mem_fetch_end = wb_cyc & wb_stb & (wb_ack | wb_err) & (&line_burst_cnt); + +assign wb_adr = {cache_write_addr[14:2], line_burst_cnt}; + +reg [`LINE_SIZE-1:0] line_collect; +reg [`CACHE_OFF_W:0] line_burst_cnt; +reg invalidate_bus_err; +always @(posedge i_clk) begin + if (i_rst) begin + wb_cyc <= 1'b0; + wb_stb <= 1'b0; + invalidate_bus_err <= 1'b0; + end else if (mem_fetch_end) begin + line_burst_cnt <= 3'b0; + wb_cyc <= 1'b0; + wb_stb <= 1'b0; + invalidate_bus_err <= invalidate_bus_err | wb_err; + end else if (wb_cyc & wb_stb & (wb_ack | wb_err)) begin + line_burst_cnt <= line_burst_cnt + 1'b1; + invalidate_bus_err <= invalidate_bus_err | wb_err; + end else if(cache_miss) begin + line_burst_cnt <= 3'b0; + wb_cyc <= 1'b1; + wb_stb <= 1'b1; + invalidate_bus_err <= 1'b0; + end +end + +reg invalidate_cache_update; // don't write pending memory request to cache after flush (fetch unit invalidates this request) +always @(posedge i_clk) begin + if (i_rst) begin + invalidate_cache_update <= 1'b0; + end else if (mem_fetch_end) begin + invalidate_cache_update <= 1'b0; + end else if (mem_cache_flush & (cache_write_valid | cache_miss)) begin + invalidate_cache_update <= 1'b1; + end +end + +wire invalidate_bus_err_w = (mem_fetch_end & wb_err) | invalidate_bus_err; + +wire [`LINE_SIZE-1:0] pre_assembled_line = {wb_i_dat, line_collect[111:0]}; +wire [`ENTRY_SIZE-1:0] cache_write_entry = {write_tag, pre_assembled_line, 1'b1}; +wire cache_we_en = mem_fetch_end & ~invalidate_cache_update & ~invalidate_bus_err_w; +always @* begin + cache_we[0] = cache_we_en; +end + + +always @(posedge i_clk) begin + case (line_burst_cnt) + default: line_collect[15:0] <= wb_i_dat; + 3'b001: line_collect[31:16] <= wb_i_dat; + 3'b010: line_collect[47:32] <= wb_i_dat; + 3'b011: line_collect[63:48] <= wb_i_dat; + 3'b100: line_collect[79:64] <= wb_i_dat; + 3'b101: line_collect[95:80] <= wb_i_dat; + 3'b110: line_collect[111:96] <= wb_i_dat; + 3'b111: line_collect[127:112] <= wb_i_dat; + endcase +end + +reg [`ENTRY_SIZE-1:0] cache_hit_entry; + +wire [`ENTRY_SIZE-1:0] entry_out = (mem_fetch_end ? {`TAG_SIZE'b0, pre_assembled_line, 1'b0} : cache_hit_entry); + +always @* begin + cache_hit_entry = cache_out[0]; +end + +wire [`CACHE_OFF_W-1:0] offset_out = (mem_fetch_end ? write_off : compare_off); +always @* begin + case (offset_out) + default: mem_data = entry_out[32:1]; + 2'b01: mem_data = entry_out[64:33]; + 2'b10: mem_data = entry_out[96:65]; + 2'b11: mem_data = entry_out[128:97]; + endcase +end + +endmodule + +`undef TAG_SIZE +`undef LINE_SIZE +`undef ENTRY_SIZE +`undef CACHE_ASSOC +`undef CACHE_ENTR_N +`undef CACHE_SETS_N +`undef CACHE_OFF_W +`undef CACHE_IDX_WIDTH +`undef CACHE_IDXES +`undef SW \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/icache/icache_ram.v b/verilog/rtl/ppcpu/rtl/icache/icache_ram.v new file mode 100644 index 0000000..06bd54f --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/icache/icache_ram.v
@@ -0,0 +1,23 @@ +module icache_ram ( +`ifdef USE_POWER_PINS + inout vccd1, // User area 1 1.8V supply + inout vssd1, // User area 1 digital ground +`endif + + input i_clk, + + input [4:0] i_addr, + input [137:0] i_data, + output reg [137:0] o_data, + input i_we +); + +reg [137:0] mem [31:0]; + +always @(posedge i_clk) begin + if(i_we) + mem[i_addr] <= i_data; + o_data <= mem[i_addr]; +end + +endmodule
diff --git a/verilog/rtl/ppcpu/rtl/interconnect/inner/dmmu.v b/verilog/rtl/ppcpu/rtl/interconnect/inner/dmmu.v new file mode 100644 index 0000000..75f908d --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/interconnect/inner/dmmu.v
@@ -0,0 +1,77 @@ +`include "config.v" + +`define IN_ADDR_W 16 +`define OUT_ADDR_W 24 + +module dmmu ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input i_clk, + input i_rst, + + input [`IN_ADDR_W-1:0] i_addr, + input [7:0] i_high_addr, + output [`OUT_ADDR_W-1:0] o_addr, + output o_cacheable, + + input [`RW-1:0] i_sr_addr, + input [`RW-1:0] i_sr_data, + input i_sr_we, + + input c_pag_en, + input c_long +); + +`define OFF_W 11 +`define PAGE_IDX_W 4 +`define PAGE_ENTRIES 16 +`define PAGE_RES_SIZE 13 +`define SR_ADDR_OFF `RW'h200 + +wire [`OFF_W-1:0] page_off = i_addr[`OFF_W-1:0]; +wire [`PAGE_IDX_W-1:0] page_idx = i_addr[`OFF_W+`PAGE_IDX_W-1:`OFF_W]; + +reg [`PAGE_RES_SIZE-1:0] page_table [`PAGE_ENTRIES-1:0]; + +wire [`PAGE_RES_SIZE-1:0] page_res = {page_table[page_idx][`PAGE_RES_SIZE-1:0]}; + +wire [`RW-1:0] sr_addr_idx = i_sr_addr-`SR_ADDR_OFF; +wire addr_in_range = (i_sr_addr >= `SR_ADDR_OFF) && (i_sr_addr < `SR_ADDR_OFF+16); +reg [7:0] long_off_reg; + +always @(posedge i_clk) begin + if (i_rst) begin + page_table[0] <= `PAGE_RES_SIZE'b0; + page_table[1] <= `PAGE_RES_SIZE'b0; + page_table[2] <= `PAGE_RES_SIZE'b0; + page_table[3] <= `PAGE_RES_SIZE'b0; + page_table[4] <= `PAGE_RES_SIZE'b0; + page_table[5] <= `PAGE_RES_SIZE'b0; + page_table[6] <= `PAGE_RES_SIZE'b0; + page_table[7] <= `PAGE_RES_SIZE'b0; + page_table[8] <= `PAGE_RES_SIZE'b0; + page_table[9] <= `PAGE_RES_SIZE'b0; + page_table[10] <= `PAGE_RES_SIZE'b0; + page_table[11] <= `PAGE_RES_SIZE'b0; + page_table[12] <= `PAGE_RES_SIZE'b0; + page_table[13] <= `PAGE_RES_SIZE'b0; + page_table[14] <= `PAGE_RES_SIZE'b0; + page_table[15] <= `PAGE_RES_SIZE'b0; + end else if (i_sr_we & addr_in_range) + page_table[sr_addr_idx[`PAGE_IDX_W-1:0]] <= i_sr_data[`PAGE_RES_SIZE-1:0]; + else if (i_sr_we & i_sr_addr == `SR_ADDR_OFF+16) + long_off_reg <= i_sr_data[7:0]; +end + +`define PAGE_DEFAULT_PREFIX 8'h10 +wire [`OUT_ADDR_W-1:0] page_disable_address = {`PAGE_DEFAULT_PREFIX, i_addr[`IN_ADDR_W-1:0]}; +wire [`OUT_ADDR_W-1:0] page_enable_address = {page_res, page_off}; +wire [`OUT_ADDR_W-1:0] long_mode_address = (i_high_addr == 8'b0 && ~i_addr[15] ? page_enable_address : {i_high_addr[7:0]+long_off_reg, i_addr}); + +assign o_addr = c_long ? long_mode_address : (c_pag_en ? page_enable_address : page_disable_address); +assign o_cacheable = (o_addr >= `OUT_ADDR_W'h100000 && o_addr < `OUT_ADDR_W'h800000); + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/interconnect/inner/immu.v b/verilog/rtl/ppcpu/rtl/interconnect/inner/immu.v new file mode 100644 index 0000000..e5a40a1 --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/interconnect/inner/immu.v
@@ -0,0 +1,83 @@ +`include "config.v" + +`define PC_ADDR_W 16 // incremented by 2 +`define OUT_ADDR_W 24 + +module immu ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input i_clk, + input i_rst, + + input [`PC_ADDR_W-1:0] i_addr, + output [`OUT_ADDR_W-1:0] o_addr, + + input [`RW-1:0] i_sr_addr, + input [`RW-1:0] i_sr_data, + input i_sr_we, + + input [`OUT_ADDR_W-`PC_ADDR_W-1:0] i_long_high_addr, + input c_long_mode, + + input c_pag_en +); + +`define OFF_W 12 +`define PAGE_IDX_W 4 +`define PAGE_ENTRIES 16 +`define PAGE_RES_SIZE 12 +`define SR_ADDR_OFF `RW'h100 + +wire [`OFF_W-1:0] page_off = i_addr[`OFF_W-1:0]; +wire [`PAGE_IDX_W-1:0] page_idx = i_addr[`OFF_W+`PAGE_IDX_W-1:`OFF_W]; + +reg [`PAGE_RES_SIZE-1:0] page_table [`PAGE_ENTRIES-1:0]; + +wire [`PAGE_RES_SIZE-1:0] page_res = {1'b1, page_table[page_idx][`PAGE_RES_SIZE-2:0]}; + +wire [`RW-1:0] sr_addr_idx = i_sr_addr-`SR_ADDR_OFF; +wire addr_in_range = (i_sr_addr >= `SR_ADDR_OFF) && (i_sr_addr < `SR_ADDR_OFF+16); +reg [7:0] high_addr_off; +always @(posedge i_clk) begin + if (i_rst) begin + page_table[0] <= `PAGE_RES_SIZE'h7fe; + page_table[1] <= `PAGE_RES_SIZE'h7ff; + page_table[2] <= `PAGE_RES_SIZE'b0; + page_table[3] <= `PAGE_RES_SIZE'b0; + page_table[4] <= `PAGE_RES_SIZE'b0; + page_table[5] <= `PAGE_RES_SIZE'b0; + page_table[6] <= `PAGE_RES_SIZE'b0; + page_table[7] <= `PAGE_RES_SIZE'b0; + page_table[8] <= `PAGE_RES_SIZE'b0; + page_table[9] <= `PAGE_RES_SIZE'b0; + page_table[10] <= `PAGE_RES_SIZE'b0; + page_table[11] <= `PAGE_RES_SIZE'b0; + page_table[12] <= `PAGE_RES_SIZE'b0; + page_table[13] <= `PAGE_RES_SIZE'b0; + page_table[14] <= `PAGE_RES_SIZE'b0; + page_table[15] <= `PAGE_RES_SIZE'b0; + end else if (i_sr_we & addr_in_range) + page_table[sr_addr_idx[`PAGE_IDX_W-1:0]] <= i_sr_data[`PAGE_RES_SIZE-1:0]; + else if (i_sr_we & i_sr_addr == `SR_ADDR_OFF+16) + high_addr_off <= i_sr_data[7:0]; +end + +`define PAGE_DEFAULT_PREFIX 8'h80 +wire [`OUT_ADDR_W-1:0] page_disable_address = {`PAGE_DEFAULT_PREFIX, i_addr[`RW-1:0]}; +wire [`OUT_ADDR_W-1:0] page_enable_address = {page_res, page_off}; + +wire [`OUT_ADDR_W-1:0] long_mode_addr = {i_long_high_addr+high_addr_off, i_addr}; + +assign o_addr = c_long_mode ? long_mode_addr : (c_pag_en ? page_enable_address : page_disable_address); + +endmodule + +`undef OFF_W +`undef PAGE_IDX_W +`undef PAGE_ENTRIES +`undef PAGE_RES_SIZE +`undef SR_ADDR_OFF +`undef PAGE_DEFAULT_PREFIX \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/interconnect/inner/intercore_sregs.v b/verilog/rtl/ppcpu/rtl/interconnect/inner/intercore_sregs.v new file mode 100644 index 0000000..04d01e0 --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/interconnect/inner/intercore_sregs.v
@@ -0,0 +1,80 @@ +`include "config.v" + +`define CORES 2 + +module intercore_sregs ( + input i_clk, + input i_rst, + + input [`RW-1:0] c0_sr_bus_addr, + input [`RW-1:0] c0_sr_bus_data_o, + output [`RW-1:0] c0_sr_bus_data_i, + input c0_sr_bus_we, + + input [`RW-1:0] c1_sr_bus_addr, + input [`RW-1:0] c1_sr_bus_data_o, + output [`RW-1:0] c1_sr_bus_data_i, + input c1_sr_bus_we, + + output c0_disable, + output c0_core_int, + + output c1_disable, + output c1_core_int +); + +`define SREG_ICINT_SET `RW'b1001 +`define SREG_ICINT_RESET `RW'b1010 +`define SREG_ICDISABLE `RW'b1011 + +wire [`CORES-1:0] ic_irq_set_read_w; +assign ic_irq_set_read_w[0] = (c0_sr_bus_addr == `SREG_ICINT_SET) && c0_sr_bus_we; +assign ic_irq_set_read_w[1] = (c1_sr_bus_addr == `SREG_ICINT_SET) && c1_sr_bus_we; + +wire [`CORES-1:0] ic_irq_reset_w; +assign ic_irq_reset_w[0] = (c0_sr_bus_addr == `SREG_ICINT_RESET) && c0_sr_bus_we; +assign ic_irq_reset_w[1] = (c1_sr_bus_addr == `SREG_ICINT_RESET) && c1_sr_bus_we; + +wire [`RW-1:0] sr_bus_dat [`CORES-1:0]; +assign sr_bus_dat[0] = c0_sr_bus_data_o; +assign sr_bus_dat[1] = c1_sr_bus_data_o; + +assign c0_sr_bus_data_i = {14'b0, ic_irq_state}; +assign c1_sr_bus_data_i = {14'b0, ic_irq_state}; + +reg [`CORES-1:0] ic_irq_state; + +always @(posedge i_clk) begin + if (i_rst) begin + ic_irq_state = 2'b0; + end else begin + if (ic_irq_reset_w[0]) + ic_irq_state = ic_irq_state & ~sr_bus_dat[0][`CORES-1:0]; + if (ic_irq_reset_w[1]) + ic_irq_state = ic_irq_state & ~sr_bus_dat[1][`CORES-1:0]; + + if (ic_irq_set_read_w[0]) + ic_irq_state = ic_irq_state | sr_bus_dat[0][`CORES-1:0]; + if (ic_irq_set_read_w[1]) + ic_irq_state = ic_irq_state | sr_bus_dat[1][`CORES-1:0]; + end +end + +wire c0_core_hold_we = (c0_sr_bus_addr == `SREG_ICDISABLE) && c0_sr_bus_we; + +assign c0_disable = 1'b0; +assign c0_core_int = ic_irq_state[0]; +assign c1_core_int = ic_irq_state[1]; + +reg core1_disable; +assign c1_disable = core1_disable; +always @(posedge i_clk) begin + if (i_rst) begin + core1_disable <= 1'b1; + end else begin + if (c0_core_hold_we) + core1_disable <= sr_bus_dat[0][1]; + end +end + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/interconnect/inner/mem_dcache_arb.v b/verilog/rtl/ppcpu/rtl/interconnect/inner/mem_dcache_arb.v new file mode 100644 index 0000000..3c3395e --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/interconnect/inner/mem_dcache_arb.v
@@ -0,0 +1,120 @@ +`include "config.v" + +module mem_dcache_arb ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + input i_clk, + input i_rst, + + output mem_req, + output reg mem_we, + input mem_ack, + output reg [`WB_ADDR_W-1:0] mem_addr, + output reg [`RW-1:0] mem_o_data, + input [`RW-1:0] mem_i_data, + output reg [1:0] mem_sel, + output reg mem_cache_enable, + input mem_exception, + + input mem0_req, + input mem0_we, + output reg mem0_ack, + input [`WB_ADDR_W-1:0] mem0_addr, + input [`RW-1:0] mem0_o_data, + output reg [`RW-1:0] mem0_i_data, + input [1:0] mem0_sel, + input mem0_cache_enable, + output reg mem0_exception, + + input mem1_req, + input mem1_we, + output reg mem1_ack, + input [`WB_ADDR_W-1:0] mem1_addr, + input [`RW-1:0] mem1_o_data, + output reg [`RW-1:0] mem1_i_data, + input [1:0] mem1_sel, + input mem1_cache_enable, + output reg mem1_exception +); + +wire request_term = mem_ack | mem_exception; + +reg select, transfer_active; +reg req0_pending, req1_pending; + +wire req_w = mem0_req | mem1_req | req0_pending | req1_pending; + +wire req_start = ~transfer_active & req_w; + +assign mem_req = req_start; + +always @(posedge i_clk) begin + if (i_rst) begin + select <= 1'b0; + transfer_active <= 1'b0; + end else if (req_start & ~request_term) begin + select <= req_sel; + transfer_active <= 1'b1; + end else if (transfer_active & request_term) begin + transfer_active <= 1'b0; + end +end + +always @(posedge i_clk) begin + if (i_rst) begin + req0_pending <= 1'b0; + req1_pending <= 1'b0; + end else if (req_start & ~req_sel) begin + req0_pending <= 1'b0; + req1_pending <= req1_pending | mem1_req; + end else if (req_start & req_sel) begin + req1_pending <= 1'b0; + req0_pending <= req0_pending | mem0_req; + end else if (transfer_active) begin + req0_pending <= (req0_pending | mem0_req) & select; + req1_pending <= (req1_pending | mem1_req) & ~select; + end +end + +reg req_sel; +always @* begin // round robin + if (select == 1'b0 && (mem1_req | req1_pending)) + req_sel = 1'b1; + else if (select == 1'b1 && (mem0_req | req0_pending)) + req_sel = 1'b0; + else if (mem0_req | req0_pending) + req_sel = 1'b0; + else if (mem1_req | req1_pending) + req_sel = 1'b1; + else + req_sel = 1'b0; +end + +wire select_wire = (req_start ? req_sel : select); + +always @(*) begin + {mem0_ack, mem0_i_data, mem0_exception, mem1_ack, mem1_i_data, mem1_exception} = 'b0; + if(~select_wire) begin + mem_we = mem0_we; + mem_addr = mem0_addr; + mem_o_data = mem0_o_data; + mem_sel = mem0_sel; + mem_cache_enable = mem0_cache_enable; + mem0_ack = mem_ack; + mem0_i_data = mem_i_data; + mem0_exception = mem_exception; + end else begin + mem_we = mem1_we; + mem_addr = mem1_addr; + mem_o_data = mem1_o_data; + mem_sel = mem1_sel; + mem_cache_enable = mem1_cache_enable; + mem1_ack = mem_ack; + mem1_i_data = mem_i_data; + mem1_exception = mem_exception; + end +end + +endmodule
diff --git a/verilog/rtl/ppcpu/rtl/interconnect/inner/wishbone_arbiter.v b/verilog/rtl/ppcpu/rtl/interconnect/inner/wishbone_arbiter.v new file mode 100644 index 0000000..db0def3 --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/interconnect/inner/wishbone_arbiter.v
@@ -0,0 +1,87 @@ +`include "config.v" + +module wishbone_arbiter ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input i_clk, + input i_rst, + + input i_wb0_cyc, + input i_wb1_cyc, + + output o_wb_cyc, + + input wb0_stb, + input [`WB_ADDR_W-1:0] wb0_adr, + input [`RW-1:0] wb0_o_dat, + input wb0_we, + input [1:0] wb0_sel, + input wb0_8_burst, wb0_4_burst, + output reg wb0_ack, + output reg wb0_err, + + input wb1_stb, + input [`WB_ADDR_W-1:0] wb1_adr, + input [`RW-1:0] wb1_o_dat, + input wb1_we, + input [1:0] wb1_sel, + input wb1_8_burst, wb1_4_burst, + output reg wb1_ack, + output reg wb1_err, + + output reg owb_stb, + output reg [`WB_ADDR_W-1:0] owb_adr, + output reg [`RW-1:0] owb_o_dat, + output reg owb_we, + output reg [1:0] owb_sel, + input owb_ack, + input owb_err, + output reg owb_8_burst, owb_4_burst +); + +reg o_sel_sig; + +wire bus_req = i_wb0_cyc | i_wb1_cyc; + +assign o_wb_cyc = (o_sel_sig ? i_wb1_cyc : i_wb0_cyc) & ~i_rst; + +always @(posedge i_clk) begin + if(i_rst) begin + o_sel_sig <= 1'b0; + end else if(~o_wb_cyc & bus_req) begin + o_sel_sig <= (i_wb0_cyc ? 1'b0 : 1'b1); + end +end + +always @(*) begin + if(~o_sel_sig) begin + owb_stb = wb0_stb; + owb_o_dat = wb0_o_dat; + owb_adr = wb0_adr; + owb_we = wb0_we; + owb_sel = wb0_sel; + wb0_ack = owb_ack; + wb0_err = owb_err; + wb1_ack = 1'b0; + wb1_err = 1'b0; + owb_4_burst = wb0_4_burst; + owb_8_burst = wb0_8_burst; + end else begin + owb_stb = wb1_stb; + owb_o_dat = wb1_o_dat; + owb_adr = wb1_adr; + owb_we = wb1_we; + owb_sel = wb1_sel; + wb1_ack = owb_ack; + wb1_err = owb_err; + wb0_ack = 1'b0; + wb0_err = 1'b0; + owb_4_burst = wb1_4_burst; + owb_8_burst = wb1_8_burst; + end +end + +endmodule
diff --git a/verilog/rtl/ppcpu/rtl/interconnect/interconnect_inner.v b/verilog/rtl/ppcpu/rtl/interconnect/interconnect_inner.v new file mode 100644 index 0000000..ecd3f42 --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/interconnect/interconnect_inner.v
@@ -0,0 +1,368 @@ +/* + * Everything connecting cores, caches together up to main wishbone bus + */ + +`include "config.v" + +`define WB_DATA_W 16 +`define WB_SEL_BITS 2 + +module interconnect_inner ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + input core_clock, + input core_reset, + + // INTERCONNECT WB OUTPUT + output inner_wb_cyc, inner_wb_stb, + output inner_wb_we, + output [`WB_ADDR_W-1:0] inner_wb_adr, + output [`WB_DATA_W-1:0] inner_wb_o_dat, + input [`WB_DATA_W-1:0] inner_wb_i_dat, + input inner_wb_ack, inner_wb_err, + output [`WB_SEL_BITS-1:0] inner_wb_sel, + output inner_wb_4_burst, inner_wb_8_burst, + input inner_ext_irq, + input inner_embed_mode, + input inner_disable, + + // CORE 0 + output c0_clk, + output c0_rst, + output c0_disable, + input [`RW-1:0] c0_o_req_addr, + input c0_o_req_active, c0_o_req_ppl_submit, + output [`I_SIZE-1:0] c0_i_req_data, + output c0_i_req_data_valid, + input [`RW-1:0] c0_dbg_r0, c0_dbg_pc, + input [`RW-1:0] c0_o_mem_addr, c0_o_mem_data, + output [`RW-1:0] c0_i_mem_data, + input c0_o_mem_req, c0_o_mem_we, + output c0_i_mem_ack, + input [`ADDR_BYTES-1:0] c0_o_mem_sel, + output c0_i_irq, + input c0_o_c_instr_page, c0_o_c_data_page, + input [`RW-1:0] c0_sr_bus_addr, c0_sr_bus_data_o, + input c0_sr_bus_we, + input c0_o_icache_flush, + output c0_i_mem_exception, + output c0_i_mc_core_int, + input c0_o_c_instr_long, + input [7:0] c0_o_instr_long_addr, + input c0_o_mem_long_mode, + input [7:0] c0_o_mem_high_addr, + output [`RW-1:0] c0_i_core_int_sreg, + + // CORE 1 + output c1_clk, + output c1_rst, + output c1_disable, + input [`RW-1:0] c1_o_req_addr, + input c1_o_req_active, c1_o_req_ppl_submit, + output [`I_SIZE-1:0] c1_i_req_data, + output c1_i_req_data_valid, + input [`RW-1:0] c1_dbg_r0, c1_dbg_pc, + input [`RW-1:0] c1_o_mem_addr, c1_o_mem_data, + output [`RW-1:0] c1_i_mem_data, + input c1_o_mem_req, c1_o_mem_we, + output c1_i_mem_ack, + input [`ADDR_BYTES-1:0] c1_o_mem_sel, + output c1_i_irq, + input c1_o_c_instr_page, c1_o_c_data_page, + input [`RW-1:0] c1_sr_bus_addr, c1_sr_bus_data_o, + input c1_sr_bus_we, + input c1_o_icache_flush, + output c1_i_mem_exception, + output c1_i_mc_core_int, + input c1_o_c_instr_long, + input [7:0] c1_o_instr_long_addr, + input c1_o_mem_long_mode, + input [7:0] c1_o_mem_high_addr, + output [`RW-1:0] c1_i_core_int_sreg, + + // ICACHE 0 + output ic0_clk, + output ic0_rst, + output ic0_mem_req, + input ic0_mem_ack, + output [`RW-1:0] ic0_mem_addr, + input [`I_SIZE-1:0] ic0_mem_data, + output ic0_mem_ppl_submit, + output ic0_mem_cache_flush, + input ic0_wb_cyc, + input ic0_wb_stb, + output [`RW-1:0] ic0_wb_i_dat, + input [`RW-1:0] ic0_wb_adr, + input ic0_wb_we, + output ic0_wb_ack, + input [1:0] ic0_wb_sel, + output ic0_wb_err, + + // ICACHE 1 + output ic1_clk, + output ic1_rst, + output ic1_mem_req, + input ic1_mem_ack, + output [`RW-1:0] ic1_mem_addr, + input [`I_SIZE-1:0] ic1_mem_data, + output ic1_mem_ppl_submit, + output ic1_mem_cache_flush, + input ic1_wb_cyc, + input ic1_wb_stb, + output [`RW-1:0] ic1_wb_i_dat, + input [`RW-1:0] ic1_wb_adr, + input ic1_wb_we, + output ic1_wb_ack, + input [1:0] ic1_wb_sel, + output ic1_wb_err, + + // DCACHE + output dcache_clk, + output dcache_rst, + output dcache_mem_req, + output dcache_mem_we, + input dcache_mem_ack, + output [`WB_ADDR_W-1:0] dcache_mem_addr, + output [`RW-1:0] dcache_mem_i_data, + input [`RW-1:0] dcache_mem_o_data, + output [1:0] dcache_mem_sel, + output dcache_mem_cache_enable, + input dcache_mem_exception, + input dcache_wb_cyc, + input dcache_wb_stb, + output [`RW-1:0] dcache_wb_i_dat, + input [`RW-1:0] dcache_wb_o_dat, + input [`WB_ADDR_W-1:0] dcache_wb_adr, + input dcache_wb_we, + input [1:0] dcache_wb_sel, + input dcache_wb_4_burst, + output dcache_wb_ack, + output dcache_wb_err + +); + +//////////////////////////////////////////////////////// +// INNER SECTION CORE > MMU > CACHES > CACHE_WB_MUXES // +//////////////////////////////////////////////////////// + +assign c0_clk = core_clock; +assign c0_rst = core_reset; +assign c1_clk = core_clock; +assign c1_rst = core_reset; + +// CORE <-> ICACHE <-> IMMU +assign ic0_clk = core_clock; +assign ic0_rst = core_reset; +assign ic0_mem_req = c0_o_req_active; +assign ic0_mem_addr = c0_o_req_addr; +assign c0_i_req_data = ic0_mem_data; +assign c0_i_req_data_valid = ic0_mem_ack; +assign ic0_mem_ppl_submit = c0_o_req_ppl_submit; +assign ic0_mem_cache_flush = c0_o_icache_flush; + +wire [`WB_ADDR_W-1:0] ic0_wb_adr_paged; +immu immu_0 ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(core_clock), .i_rst(core_reset), + .i_addr(ic0_wb_adr), .o_addr(ic0_wb_adr_paged), + .i_sr_addr(c0_sr_bus_addr), .i_sr_data(c0_sr_bus_data_o), .i_sr_we(c0_sr_bus_we), + .c_pag_en(c0_o_c_instr_page & ~inner_embed_mode), + .c_long_mode(c0_o_c_instr_long), + .i_long_high_addr(c0_o_instr_long_addr) +); + +assign ic1_clk = core_clock; +assign ic1_rst = core_reset; +assign ic1_mem_req = c1_o_req_active; +assign ic1_mem_addr = c1_o_req_addr; +assign c1_i_req_data = ic1_mem_data; +assign c1_i_req_data_valid = ic1_mem_ack; +assign ic1_mem_ppl_submit = c1_o_req_ppl_submit; +assign ic1_mem_cache_flush = c1_o_icache_flush; + +wire [`WB_ADDR_W-1:0] ic1_wb_adr_paged; +immu immu_1 ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(core_clock), .i_rst(core_reset), + .i_addr(ic1_wb_adr), .o_addr(ic1_wb_adr_paged), + .i_sr_addr(c1_sr_bus_addr), .i_sr_data(c1_sr_bus_data_o), .i_sr_we(c1_sr_bus_we), + .c_pag_en(c1_o_c_instr_page & ~inner_embed_mode), + .c_long_mode(c1_o_c_instr_long), + .i_long_high_addr(c1_o_instr_long_addr) +); + + + +// ICACHE{0,1} ARBITER +wire icache_wb_cyc, icache_wb_stb; +wire icache_wb_we; +wire icache_wb_ack, icache_wb_err; +wire [`WB_ADDR_W-1:0] icache_wb_adr; +wire [`WB_DATA_W-1:0] icache_wb_o_dat, icache_wb_i_dat; +wire [`WB_SEL_BITS-1:0] icache_wb_sel; +wire icache_wb_4_burst, icache_wb_8_burst; + +assign ic0_wb_i_dat = icache_wb_i_dat; +assign ic1_wb_i_dat = icache_wb_i_dat; + +wishbone_arbiter icache_arbiter ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(core_clock), .i_rst(core_reset), + + .o_wb_cyc(icache_wb_cyc), + .owb_stb(icache_wb_stb), + .owb_we(icache_wb_we), + .owb_ack(icache_wb_ack), + .owb_adr(icache_wb_adr), + .owb_sel(icache_wb_sel), + .owb_err(icache_wb_err), + .owb_o_dat(icache_wb_o_dat), + .owb_4_burst(icache_wb_4_burst), + .owb_8_burst(icache_wb_8_burst), + + .i_wb0_cyc(ic0_wb_cyc), + .wb0_stb(ic0_wb_stb), + .wb0_we(ic0_wb_we), + .wb0_ack(ic0_wb_ack), + .wb0_adr(ic0_wb_adr_paged), + .wb0_sel(ic0_wb_sel), + .wb0_err(ic0_wb_err), + .wb0_o_dat(`RW'b0), + .wb0_4_burst(1'b0), + .wb0_8_burst(1'b1), + + .i_wb1_cyc(ic1_wb_cyc), + .wb1_stb(ic1_wb_stb), + .wb1_we(ic1_wb_we), + .wb1_ack(ic1_wb_ack), + .wb1_adr(ic1_wb_adr_paged), + .wb1_sel(ic1_wb_sel), + .wb1_err(ic1_wb_err), + .wb1_o_dat(`RW'b0), + .wb1_4_burst(1'b0), + .wb1_8_burst(1'b1) +); + +// CORE -> DMMU -> DCACHE BUS ARBITER + +wire [`WB_ADDR_W-1:0] c0_mem_addr_paged; +wire c0_mmu_data_cacheable; +dmmu dmmu0 ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(core_clock), .i_rst(core_reset), + .i_addr(c0_o_mem_addr), .o_addr(c0_mem_addr_paged), + .i_sr_addr(c0_sr_bus_addr), .i_sr_data(c0_sr_bus_data_o), .i_sr_we(c0_sr_bus_we), + .c_pag_en(c0_o_c_data_page), .o_cacheable(c0_mmu_data_cacheable), .c_long(c0_o_mem_long_mode), .i_high_addr(c0_o_mem_high_addr) +); + +wire [`WB_ADDR_W-1:0] c1_mem_addr_paged; +wire c1_mmu_data_cacheable; +dmmu dmmu1 ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(core_clock), .i_rst(core_reset), + .i_addr(c1_o_mem_addr), .o_addr(c1_mem_addr_paged), + .i_sr_addr(c1_sr_bus_addr), .i_sr_data(c1_sr_bus_data_o), .i_sr_we(c1_sr_bus_we), + .c_pag_en(c1_o_c_data_page), .o_cacheable(c1_mmu_data_cacheable), .c_long(c1_o_mem_long_mode), .i_high_addr(c1_o_mem_high_addr) +); + +mem_dcache_arb mem_dcache_arb ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(core_clock), + .i_rst(core_reset), + + .mem_req(dcache_mem_req), .mem_we(dcache_mem_we), .mem_ack(dcache_mem_ack), .mem_addr(dcache_mem_addr), .mem_i_data(dcache_mem_o_data), .mem_o_data(dcache_mem_i_data), .mem_sel(dcache_mem_sel), .mem_cache_enable(dcache_mem_cache_enable), .mem_exception(dcache_mem_exception), + .mem0_req(c0_o_mem_req), .mem0_we(c0_o_mem_we), .mem0_ack(c0_i_mem_ack), .mem0_addr(c0_mem_addr_paged), .mem0_i_data(c0_i_mem_data), .mem0_o_data(c0_o_mem_data), .mem0_sel(c0_o_mem_sel), .mem0_cache_enable(c0_mmu_data_cacheable), .mem0_exception(c0_i_mem_exception), + .mem1_req(c1_o_mem_req), .mem1_we(c1_o_mem_we), .mem1_ack(c1_i_mem_ack), .mem1_addr(c1_mem_addr_paged), .mem1_i_data(c1_i_mem_data), .mem1_o_data(c1_o_mem_data), .mem1_sel(c1_o_mem_sel), .mem1_cache_enable(c1_mmu_data_cacheable), .mem1_exception(c1_i_mem_exception) +); + +assign dcache_clk = core_clock; +assign dcache_rst = core_reset; + +// {DCACHE, ICACHE} WB ARBITER + +assign icache_wb_i_dat = inner_wb_i_dat; +assign dcache_wb_i_dat = inner_wb_i_dat; + +wishbone_arbiter inner_wb_arbiter ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(core_clock), .i_rst(core_reset), + + .o_wb_cyc(inner_wb_cyc), + .owb_stb(inner_wb_stb), + .owb_we(inner_wb_we), + .owb_ack(inner_wb_ack), + .owb_adr(inner_wb_adr), + .owb_sel(inner_wb_sel), + .owb_err(inner_wb_err), + .owb_o_dat(inner_wb_o_dat), + .owb_4_burst(inner_wb_4_burst), + .owb_8_burst(inner_wb_8_burst), + + .i_wb0_cyc(icache_wb_cyc), + .wb0_stb(icache_wb_stb), + .wb0_we(icache_wb_we), + .wb0_ack(icache_wb_ack), + .wb0_adr(icache_wb_adr), + .wb0_sel(icache_wb_sel), + .wb0_err(icache_wb_err), + .wb0_o_dat(icache_wb_o_dat), + .wb0_4_burst(icache_wb_4_burst), + .wb0_8_burst(icache_wb_8_burst), + + .i_wb1_cyc(dcache_wb_cyc), + .wb1_stb(dcache_wb_stb), + .wb1_we(dcache_wb_we), + .wb1_ack(dcache_wb_ack), + .wb1_adr(dcache_wb_adr), + .wb1_sel(dcache_wb_sel), + .wb1_err(dcache_wb_err), + .wb1_o_dat(dcache_wb_o_dat), + .wb1_4_burst(dcache_wb_4_burst), + .wb1_8_burst(1'b0) +); + +// INTERCORE CONTROL + +assign c0_i_irq = inner_ext_irq; +assign c1_i_irq = 1'b0; +wire c0_sc_disable, c1_sc_disable; +assign c0_disable = c0_sc_disable | inner_disable; +assign c1_disable = c1_sc_disable | inner_disable; + +intercore_sregs icore_sregs ( + .i_clk(core_clock), + .i_rst(core_reset), + + .c0_sr_bus_addr(c0_sr_bus_addr), + .c0_sr_bus_data_o(c0_sr_bus_data_o), + .c0_sr_bus_data_i(c0_i_core_int_sreg), + .c0_sr_bus_we(c0_sr_bus_we), + + .c1_sr_bus_addr(c1_sr_bus_addr), + .c1_sr_bus_data_o(c1_sr_bus_data_o), + .c1_sr_bus_data_i(c1_i_core_int_sreg), + .c1_sr_bus_we(c1_sr_bus_we), + + .c0_disable(c0_sc_disable), + .c0_core_int(c0_i_mc_core_int), + .c1_disable(c1_sc_disable), + .c1_core_int(c1_i_mc_core_int) +); + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/interconnect/interconnect_outer.v b/verilog/rtl/ppcpu/rtl/interconnect/interconnect_outer.v new file mode 100644 index 0000000..107aa32 --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/interconnect/interconnect_outer.v
@@ -0,0 +1,478 @@ +`include "config.v" + +`define MPRJ_IO_PADS 38 + +`define WB_DATA_W 16 +`define WB_SEL_BITS 2 + +module interconnect_outer ( + // IOs + input [`MPRJ_IO_PADS-1:0] m_io_in, + output [`MPRJ_IO_PADS-1:0] m_io_out, + output [`MPRJ_IO_PADS-1:0] m_io_oeb, + + // Wishbone from managment core + input mgt_wb_clk_i, + input mgt_wb_rst_i, + input mgt_wb_stb_i, + input mgt_wb_cyc_i, + input mgt_wb_we_i, + input [3:0] mgt_wb_sel_i, + input [31:0] mgt_wb_dat_i, + input [31:0] mgt_wb_adr_i, + output mgt_wb_ack_o, + output [31:0] mgt_wb_dat_o, + + // Logic Analyzer Signals + input [127:0] la_data_in, + output [127:0] la_data_out, + input [127:0] la_oenb, + + // IRQ + output [2:0] irq, + + // Independent clock (on independent integer divider) + input user_clock2, + + // Lower Interconnect + output inner_clock, inner_reset, + input inner_wb_cyc, inner_wb_stb, + input inner_wb_we, + input [`WB_ADDR_W-1:0] inner_wb_adr, + input [`WB_DATA_W-1:0] inner_wb_o_dat, + output [`WB_DATA_W-1:0] inner_wb_i_dat, + output inner_wb_ack, inner_wb_err, + input [`WB_SEL_BITS-1:0] inner_wb_sel, + input inner_wb_4_burst, inner_wb_8_burst, + output inner_ext_irq, + output inner_embed_mode, + output inner_disable, + + // Internal ram + output iram_clk, + output [7:0] iram_addr, + output [31:0] iram_i_data, + input [31:0] iram_o_data, + output iram_we, iram_csb, + output [3:0] iram_w_mask, + input iram1_clk, + output [7:0] iram1_addr, + input [31:0] iram1_dout, + input iram1_csb +); + +// Use user_clock2 as clock. It can be used independently from mgmt cpu and is not limited to 40Mhz by it. +// Wishbone port is not used, it is the same. Default freq is 10MHz +wire soc_clock = user_clock2; +wire soc_reset = mgt_wb_rst_i; + +wire core_clock = soc_clock; +wire soft_reset = (~la_oenb[0]) & la_data_in[0]; // in future & embed mode +wire core_reset_us = soc_reset | soft_reset; +wire core_reset; // synced + +assign inner_clock = core_clock; +assign inner_reset = core_reset; + +// MASTER CW BUS + +wire [`RW-1:0] cw_io_i; // S>M +wire [`RW-1:0] cw_io_o; // M>S +wire cw_req; +wire cw_dir; +wire cw_ack; +wire cw_err; +wire cw_clk; // ouputs for bridge +wire cw_rst; + +// PIN ASSIGNMENTS +// pins [4:0] are reserved to mgmt during boot + +assign m_io_out[8] = cw_req; +assign m_io_out[9] = cw_dir; +assign m_io_out[25:10] = cw_io_o; +assign cw_io_i = m_io_in[25:10]; +assign cw_ack = m_io_in[26]; +assign cw_err = m_io_in[27]; +assign m_io_out[28] = cw_clk; +assign m_io_out[29] = cw_rst; // reset out + +wire ext_irq = m_io_in[30]; + +wire cs_split_clock = m_io_in[31]; +wire core_disable = m_io_in[32]; +wire embed_mode = m_io_in[33]; + +assign spi_clk = m_io_in[34]; +assign spi_mosi = m_io_in[35]; +assign m_io_out[36] = spi_miso; + +assign gpio_in = m_io_in[7:0]; +assign m_io_out[7:0] = gpio_out; +assign m_io_out[37] = 1'b1; + +assign m_io_oeb[7:0] = gpio_dir; +assign m_io_oeb[9:8] = 2'b0; +assign m_io_oeb[25:10] = {16{cw_dir}}; +assign m_io_oeb[27:26] = 2'b11; +assign m_io_oeb[29:28] = 2'b00; +assign m_io_oeb[33:30] = 4'b1111; +assign m_io_oeb[35:34] = 2'b11; +assign m_io_oeb[36] = 1'b0; +assign m_io_oeb[37] = 1'b0; + +assign inner_embed_mode = embed_mode_synced; +assign inner_disable = core_disable_synced; + +assign irq = 2'b00; + +// CLOCKING +`define CLK_DIV_ADDR `WB_ADDR_W'h001001 +wire clk_div_write = (m_wb_cyc & m_wb_ack & m_wb_we & wb_tsel_clk); +clk_div clk_div ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(soc_clock), + .i_rst(core_reset), + .o_clk(cw_clk), + .div(m_wb_o_dat[3:0]), + .div_we(clk_div_write), + .clock_sel(cs_split_clock_synced) +); + +// WISHBONE CLOCK DOMAIN CROSSING + +wire cwclk_wb_cyc; +wire cwclk_wb_stb; +wire [`WB_ADDR_W-1:0] cwclk_wb_adr; +wire cwclk_wb_we; +wire [`WB_SEL_BITS-1:0] cwclk_wb_sel; +wire [`WB_DATA_W-1:0] cwclk_wb_o_dat; +wire[`WB_DATA_W-1:0] cwclk_wb_i_dat = cw_mux_wb_i_dat; +wire cwclk_wb_ack = cw_mux_wb_ack; +wire cwclk_wb_err = cw_mux_wb_err; +wire cwclk_wb_8_burst, cwclk_wb_4_burst; + +// CROSS_CLK OUPUT SIGNALS FROM M_WB SIDE FOR MUXING +wire [`WB_DATA_W-1:0] m_wb_i_dat_cross; +wire m_wb_ack_cross, m_wb_err_cross; + +wb_cross_clk wb_cross_clk ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .clk_m(core_clock), + .clk_s(cw_clk), + .m_rst(core_reset), + .s_rst(cw_rst), + + .m_wb_cyc(m_wb_cyc), + .m_wb_stb(m_wb_stb & wb_tsel_cw), + .m_wb_o_dat(m_wb_o_dat), + .m_wb_i_dat(m_wb_i_dat_cross), + .m_wb_adr(m_wb_adr), + .m_wb_we(m_wb_we), + .m_wb_ack(m_wb_ack_cross), + .m_wb_err(m_wb_err_cross), + .m_wb_sel(m_wb_sel), + .m_wb_4_burst(m_wb_4_burst), + .m_wb_8_burst(m_wb_8_burst), + + .s_wb_cyc(cwclk_wb_cyc), + .s_wb_stb(cwclk_wb_stb), + .s_wb_o_dat(cwclk_wb_o_dat), + .s_wb_i_dat(cwclk_wb_i_dat), + .s_wb_adr(cwclk_wb_adr), + .s_wb_we(cwclk_wb_we), + .s_wb_ack(cwclk_wb_ack), + .s_wb_err(cwclk_wb_err), + .s_wb_sel(cwclk_wb_sel), + .s_wb_4_burst(cwclk_wb_4_burst), + .s_wb_8_burst(cwclk_wb_8_burst) +); + +// clock split bypass + +wire cw_mux_wb_cyc; +wire cw_mux_wb_stb; +wire [`WB_ADDR_W-1:0] cw_mux_wb_adr; +wire cw_mux_wb_we; +wire [`WB_SEL_BITS-1:0] cw_mux_wb_sel; +wire [`WB_DATA_W-1:0] cw_mux_wb_o_dat; +wire[`WB_DATA_W-1:0] cw_mux_wb_i_dat; +wire cw_mux_wb_ack; +wire cw_mux_wb_err; +wire cw_mux_wb_8_burst, cw_mux_wb_4_burst; +wire cw_out_wb_ack; +wire cw_out_wb_err; +wire [`RW-1:0] cw_out_wb_i_dat; + +assign cw_mux_wb_cyc = (cs_split_clock_synced ? cwclk_wb_cyc : m_wb_cyc); +assign cw_mux_wb_stb = (cs_split_clock_synced ? cwclk_wb_stb : m_wb_stb); +assign cw_mux_wb_o_dat = (cs_split_clock_synced ? cwclk_wb_o_dat : m_wb_o_dat); +assign cw_mux_wb_adr = (cs_split_clock_synced ? cwclk_wb_adr : m_wb_adr); +assign cw_mux_wb_we = (cs_split_clock_synced ? cwclk_wb_we : m_wb_we); +assign cw_mux_wb_sel = (cs_split_clock_synced ? cwclk_wb_sel : m_wb_sel); +assign cw_mux_wb_8_burst = (cs_split_clock_synced ? cwclk_wb_8_burst : m_wb_8_burst); +assign cw_mux_wb_4_burst = (cs_split_clock_synced ? cwclk_wb_4_burst : m_wb_4_burst); +assign cw_out_wb_ack = (cs_split_clock_synced ? m_wb_ack_cross : cw_mux_wb_ack); +assign cw_out_wb_err = (cs_split_clock_synced ? m_wb_err_cross : cw_mux_wb_err); +assign cw_out_wb_i_dat = (cs_split_clock_synced ? m_wb_i_dat_cross : cw_mux_wb_i_dat); + + +// CW BUS CONVERTER + +wb_compressor wb_compressor ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(cw_clk), + .i_rst(cw_rst), + + .cw_io_i(cw_io_i), + .cw_io_o(cw_io_o), + .cw_req(cw_req), + .cw_dir(cw_dir), + .cw_ack(cw_ack), + .cw_err(cw_err), + + .wb_cyc(cw_mux_wb_cyc), + .wb_stb(cw_mux_wb_stb & wb_tsel_cw), + .wb_o_dat(cw_mux_wb_o_dat), + .wb_i_dat(cw_mux_wb_i_dat), + .wb_adr(cw_mux_wb_adr), + .wb_we(cw_mux_wb_we), + .wb_ack(cw_mux_wb_ack), + .wb_err(cw_mux_wb_err), + .wb_sel(cw_mux_wb_sel), + .wb_4_burst(cw_mux_wb_4_burst), + .wb_8_burst(cw_mux_wb_8_burst) +); + +// EXT SPI WISHBONE MASTER +wire spi_wb_cyc; +wire spi_wb_stb; +wire [`WB_ADDR_W-1:0] spi_wb_adr; +wire spi_wb_we; +wire [`WB_SEL_BITS-1:0] spi_wb_sel; +wire [`WB_DATA_W-1:0] spi_wb_o_dat; +wire [`WB_DATA_W-1:0] spi_wb_i_dat; +wire spi_wb_ack; +wire spi_wb_err; +wire spi_wb_8_burst=1'b0, spi_wb_4_burst=1'b0; + +wire spi_clk; +wire spi_mosi; +wire spi_miso; + +sspi sspi ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(core_clock), + .i_rst(core_reset), + + .spi_clk(spi_clk), + .spi_mosi(spi_mosi), + .spi_miso(spi_miso), + + .wb_cyc(spi_wb_cyc), + .wb_stb(spi_wb_stb), + .wb_o_dat(spi_wb_o_dat), + .wb_i_dat(spi_wb_i_dat), + .wb_adr(spi_wb_adr), + .wb_we(spi_wb_we), + .wb_ack(spi_wb_ack), + .wb_err(spi_wb_err), + .wb_sel(spi_wb_sel) +); + +wire gpio_wb_stb; +wire [`RW-1:0] gpio_wb_o_dat; +wire gpio_wb_ack; +wire [7:0] gpio_in, gpio_out, gpio_dir; + +gpio #(.N(8)) gpio ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(core_clock), + .i_rst(core_reset), + + .gpio_in(gpio_in), + .gpio_out(gpio_out), + .gpio_dir(gpio_dir), + + .wb_cyc(m_wb_cyc), + .wb_stb(m_wb_stb), + .wb_i_dat(m_wb_o_dat), + .wb_o_dat(gpio_wb_o_dat), + .wb_adr(m_wb_adr), + .wb_we(m_wb_we), + .wb_ack(gpio_wb_ack) +); + +// WISHBONE ARBITTER INNER BUS, EXT SPI BUS +wire m_wb_cyc, m_wb_stb; +wire m_wb_we; +wire [`WB_ADDR_W-1:0] m_wb_adr; +wire [`WB_DATA_W-1:0] m_wb_o_dat; +wire [`WB_SEL_BITS-1:0] m_wb_sel; +wire m_wb_4_burst, m_wb_8_burst; +reg m_wb_ack, m_wb_err; +reg [`WB_DATA_W-1:0] m_wb_i_dat; + +assign spi_wb_i_dat = m_wb_i_dat; +assign inner_wb_i_dat = m_wb_i_dat; + +wishbone_arbiter m_arbiter ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(core_clock), .i_rst(core_reset), + + .o_wb_cyc(m_wb_cyc), + .owb_stb(m_wb_stb), + .owb_we(m_wb_we), + .owb_ack(m_wb_ack), + .owb_adr(m_wb_adr), + .owb_sel(m_wb_sel), + .owb_err(m_wb_err), + .owb_o_dat(m_wb_o_dat), + .owb_4_burst(m_wb_4_burst), + .owb_8_burst(m_wb_8_burst), + + .i_wb0_cyc(spi_wb_cyc), + .wb0_stb(spi_wb_stb), + .wb0_we(spi_wb_we), + .wb0_ack(spi_wb_ack), + .wb0_adr(spi_wb_adr), + .wb0_sel(spi_wb_sel), + .wb0_err(spi_wb_err), + .wb0_o_dat(spi_wb_o_dat), + .wb0_4_burst(spi_wb_4_burst), + .wb0_8_burst(spi_wb_8_burst), + + .i_wb1_cyc(inner_wb_cyc), + .wb1_stb(inner_wb_stb), + .wb1_we(inner_wb_we), + .wb1_ack(inner_wb_ack), + .wb1_adr(inner_wb_adr), + .wb1_sel(inner_wb_sel), + .wb1_err(inner_wb_err), + .wb1_o_dat(inner_wb_o_dat), + .wb1_4_burst(inner_wb_4_burst), + .wb1_8_burst(inner_wb_8_burst) +); + +// INTERNAL RAM (for easier tesing in embed mode) +wire [`RW-1:0] iram_wb_i_dat; + +assign iram_clk = core_clock; +assign iram_addr = m_wb_adr[7:0]; +assign iram_i_data = {16'b0,m_wb_o_dat}; +assign iram_wb_i_dat = iram_o_data[15:0]; +assign iram_we = m_wb_cyc & m_wb_stb & m_wb_we & wb_tsel_iram; +assign iram_csb = 1'b1; +assign iram1_clk = 1'b0; +assign iram1_csb = 1'b0; +assign iram1_addr = 1'b0; + +reg iram_wb_ack; +always @(posedge core_clock) begin + if (core_reset) iram_wb_ack <= 1'b0; + else iram_wb_ack <= m_wb_cyc & m_wb_stb & wb_tsel_iram & ~m_wb_ack; // 1 cyc delay +end + +// WISHBONE TARGET SELECT +`define NE_INTMEM_BEGIN 24'h7ffe00 +`define E_PROG_START 24'h800000 +`define E_MEM_START 24'h100000 +`define INTMEM_SIZE 24'h100 +`define GPIO_START 24'h001010 +`define GPIO_END 24'h001012 + +wire wb_tsel_cw = (~embed_mode && (m_wb_adr != `CLK_DIV_ADDR) && (~((m_wb_adr >= `GPIO_START) && (m_wb_adr <= `GPIO_END))) && ((m_wb_adr < `NE_INTMEM_BEGIN) || (m_wb_adr >= `NE_INTMEM_BEGIN+`INTMEM_SIZE))); +wire wb_tsel_iram = (~embed_mode && (m_wb_adr >= `NE_INTMEM_BEGIN) && (m_wb_adr < `NE_INTMEM_BEGIN+`INTMEM_SIZE)) + || (embed_mode && (((m_wb_adr >= `E_PROG_START) && (m_wb_adr < `E_PROG_START+`INTMEM_SIZE)) + || (m_wb_adr >= `E_MEM_START) && (m_wb_adr < `E_MEM_START+`INTMEM_SIZE))); +wire wb_tsel_clk = (m_wb_adr == `CLK_DIV_ADDR); +wire wb_tsel_gpio = (m_wb_adr >= `GPIO_START) && (m_wb_adr <= `GPIO_END); + +always @(*) begin + if (wb_tsel_cw) begin + m_wb_ack = cw_out_wb_ack; + m_wb_err = cw_out_wb_err; + m_wb_i_dat = cw_out_wb_i_dat; + end else if (wb_tsel_iram) begin + m_wb_ack = iram_wb_ack; + m_wb_err = 1'b0; + m_wb_i_dat = iram_wb_i_dat; + end else if (wb_tsel_clk) begin + m_wb_ack = 1'b1; + m_wb_err = 1'b0; + m_wb_i_dat = `RW'b0; + end else if (wb_tsel_gpio) begin + m_wb_ack = gpio_wb_ack; + m_wb_err = 1'b0; + m_wb_i_dat = gpio_wb_o_dat; + end else begin + m_wb_ack = 1'b0; + m_wb_err = 1'b1; + m_wb_i_dat = `RW'b0; + end +end + +// SYNCHRONIZERS + +reset_sync rst_cw_sync ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(cw_clk), + .i_rst(core_reset_us), + .o_rst(cw_rst) +); + +reset_sync rst_soc_sync ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(soc_clock), + .i_rst(core_reset_us), + .o_rst(core_reset) +); + +// sync external gpio signals + +assign inner_ext_irq = irq_s_ff[1]; +reg [1:0] irq_s_ff; +always @(posedge core_clock) begin + irq_s_ff[0] <= ext_irq; + irq_s_ff[1] <= irq_s_ff[0]; +end + +wire cs_split_clock_synced = split_s_ff[1]; +wire core_disable_synced = disable_s_ff[1]; +wire embed_mode_synced = embed_s_ff[1]; + +reg [1:0] split_s_ff; +always @(posedge core_clock) begin + split_s_ff[0] <= cs_split_clock; + split_s_ff[1] <= split_s_ff[0]; +end + +reg [1:0] disable_s_ff; +always @(posedge core_clock) begin + disable_s_ff[0] <= core_disable; + disable_s_ff[1] <= disable_s_ff[0]; +end + +reg [1:0] embed_s_ff; +always @(posedge core_clock) begin + embed_s_ff[0] <= embed_mode; + embed_s_ff[1] <= embed_s_ff[0]; +end + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/interconnect/outer/clk_div.v b/verilog/rtl/ppcpu/rtl/interconnect/outer/clk_div.v new file mode 100644 index 0000000..f58bb6a --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/interconnect/outer/clk_div.v
@@ -0,0 +1,61 @@ +`define MAX_DIV 16 +`define MAX_DIV_LOG 4 + +module clk_div ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + input i_clk, + input i_rst, + + output o_clk, + + input [`MAX_DIV_LOG-1:0] div, + input div_we, + input clock_sel +); + +// ADD 130 buff + +reg [`MAX_DIV-1:0] cnt; +reg [`MAX_DIV_LOG-1:0] curr_div, next_div_buff; +reg next_div_val; + +reg res_clk; +assign o_clk = (clock_sel_r ? res_clk : i_clk); + +always @(posedge i_clk) begin + if (~cnt[curr_div]) begin + cnt <= cnt + `MAX_DIV'b1; + end else begin + cnt <= `MAX_DIV'b0; + end +end + +always @(posedge i_clk) begin + if (cnt[curr_div]) + res_clk <= ~res_clk; +end + +always @(posedge i_clk) begin + if(i_rst) begin + curr_div <= `MAX_DIV_LOG'b0101; // default caravel clock is 10MHz / 2**(5+1)=150kHz + next_div_val <= 1'b0; + end else begin + if(cnt[curr_div] & next_div_val) begin + curr_div <= next_div_buff; + next_div_val <= 1'b0; + end + if (div_we) begin + next_div_buff <= div; + next_div_val <= 1'b1; + end + end +end + +reg clock_sel_r; +always @(posedge i_clk) + clock_sel_r <= clock_sel; + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/interconnect/outer/ff_mb_sync.v b/verilog/rtl/ppcpu/rtl/interconnect/outer/ff_mb_sync.v new file mode 100644 index 0000000..adecc41 --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/interconnect/outer/ff_mb_sync.v
@@ -0,0 +1,57 @@ +module ff_mb_sync #(parameter DATA_W = 1) ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input src_clk, + input dst_clk, + + input src_rst, + input dst_rst, + + input [DATA_W-1:0] i_data, + output [DATA_W-1:0] o_data, + + input i_xfer_req // transfer is possible only one time per 3 cycles +); + +reg [DATA_W-1:0] s_data_ff; +always @(posedge src_clk) begin + if (i_xfer_req) + s_data_ff <= i_data; +end + +reg s_xfer_xor_flag; +always @(posedge src_clk) begin + if(src_rst) + s_xfer_xor_flag <= 1'b0; + else if (i_xfer_req) + s_xfer_xor_flag <= ~s_xfer_xor_flag; +end + +reg [2:0] d_xfer_xor_sync; +always @(posedge dst_clk) begin + if(dst_rst) begin + d_xfer_xor_sync[2:0] <= 3'b0; + end else begin + d_xfer_xor_sync[0] <= s_xfer_xor_flag; + d_xfer_xor_sync[1] <= d_xfer_xor_sync[0]; + d_xfer_xor_sync[2] <= d_xfer_xor_sync[1]; + end +end + +wire d_xfer_flag = d_xfer_xor_sync[1] ^ d_xfer_xor_sync[2]; + +reg [DATA_W-1:0] d_data; +always @(posedge dst_clk) begin + if(dst_rst) + d_data <= 'b0; + else if(d_xfer_flag) + d_data <= s_data_ff; +end + +assign o_data = d_data; + + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/interconnect/outer/reset_sync.v b/verilog/rtl/ppcpu/rtl/interconnect/outer/reset_sync.v new file mode 100644 index 0000000..41bbd49 --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/interconnect/outer/reset_sync.v
@@ -0,0 +1,24 @@ +module reset_sync ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input i_rst, + output o_rst, + input i_clk +); + +assign o_rst = reset_sync_ff[1]; +reg [1:0] reset_sync_ff; +always @(posedge i_clk or posedge i_rst) begin + if (i_rst) begin + reset_sync_ff[0] <= 1'b1; + reset_sync_ff[1] <= 1'b1; + end else begin + reset_sync_ff[0] <= 1'b0; + reset_sync_ff[1] <= reset_sync_ff[0]; + end +end + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/interconnect/outer/wb_compressor.v b/verilog/rtl/ppcpu/rtl/interconnect/outer/wb_compressor.v new file mode 100644 index 0000000..84f3322 --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/interconnect/outer/wb_compressor.v
@@ -0,0 +1,161 @@ +`include "config.v" + +module wb_compressor ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input i_clk, + input i_rst, + + input wb_cyc, + input wb_stb, + input [`WB_ADDR_W-1:0] wb_adr, + input [`RW-1:0] wb_o_dat, + output reg [`RW-1:0] wb_i_dat, + input wb_we, + input [1:0] wb_sel, + input wb_8_burst, wb_4_burst, + output reg wb_ack, + output reg wb_err, + + output reg [`RW-1:0] cw_io_o, + input [`RW-1:0] cw_io_i, + output reg cw_req, + output reg cw_dir, + input cw_ack, + input cw_err +); + + +`define SW 4 +`define S_IDLE `SW'b0 +`define S_HDR_1 `SW'b1 +`define S_WACK `SW'b10 +`define S_DATA_R `SW'b11 +`define S_DATA_W `SW'b101 +`define S_DATA_WT `SW'b111 +`define S_WC `SW'b100 +reg [`SW-1:0] state; + +wire [3:0] cyc_type = (wb_8_burst ? 4'b001 : (wb_4_burst ? 4'b010 : 4'b000)); +wire [`RW-1:0] header_0 = {wb_adr[`WB_ADDR_W-1:`RW], cyc_type, wb_we, wb_sel, 1'b1}; + +reg [`WB_ADDR_W-1:0] l_wb_adr; +wire [`WB_ADDR_W-1:0] wb_exp_adr = l_wb_adr + {20'b0, burst_cnt}; +reg [`WB_ADDR_W-1:0] ack_exp_adr; + +reg l_we; + +`define MAX_BRST_LOG 3 +reg [`MAX_BRST_LOG-1:0] burst_end, burst_cnt; + +wire xfer_ack = ((cw_ack | cw_err) && wb_cyc && wb_stb); +wire ignored_addr = wb_adr < `WB_ADDR_W'h002000; + +always @(posedge i_clk) begin + if (i_rst) begin + state <= `S_IDLE; + wb_ack <= 1'b0; + cw_req <= 1'b0; + cw_dir <= 1'b0; + cw_io_o <= 'b0; + end else begin + case (state) + default: begin + if (wb_cyc & wb_stb & ~ignored_addr) begin + state <= `S_HDR_1; + cw_io_o <= header_0; + cw_dir <= 1'b0; + cw_req <= 1'b1; + l_we <= wb_we; + burst_end <= (wb_8_burst ? 3'd7 : (wb_4_burst ? 3'd3 : 3'b0)); + + burst_cnt <= `MAX_BRST_LOG'b0; + end + end + `S_HDR_1: begin + state <= `S_WACK; + cw_req <= 1'b0; + cw_io_o <= wb_adr[`RW-1:0]; + end + `S_WACK: begin + if (cw_ack) + state <= (l_we ? `S_DATA_W : `S_DATA_R); + cw_io_o <= wb_o_dat; + end + `S_DATA_R: begin + cw_dir <= 1'b1; + cw_io_o <= wb_o_dat; + + wb_ack <= 1'b0; + wb_err <= 1'b0; + + if (xfer_ack && burst_cnt != burst_end) begin + burst_cnt <= burst_cnt + 1'b1; + wb_ack <= cw_ack; // It is good idea to break combinational path at end of IC + wb_err <= cw_err; + wb_i_dat <= cw_io_i; + ack_exp_adr <= wb_exp_adr; + end else if (xfer_ack && burst_cnt == burst_end) begin + burst_cnt <= `MAX_BRST_LOG'b0; + wb_ack <= cw_ack; + wb_err <= cw_err; + wb_i_dat <= cw_io_i; + state <= `S_WC; + cw_dir <= ~cw_dir; + ack_exp_adr <= wb_exp_adr; + end + end + `S_DATA_W: begin + cw_dir <= 1'b0; + wb_ack <= 1'b0; + wb_err <= 1'b0; + cw_req <= 1'b0; + + if (xfer_ack && burst_cnt != burst_end) begin + burst_cnt <= burst_cnt + 1'b1; + wb_ack <= cw_ack; + wb_err <= cw_err; + wb_i_dat <= cw_io_i; + ack_exp_adr <= wb_exp_adr; + state <= `S_DATA_WT; + end else if (xfer_ack && burst_cnt == burst_end) begin + burst_cnt <= `MAX_BRST_LOG'b0; + wb_ack <= cw_ack; + wb_err <= cw_err; + wb_i_dat <= cw_io_i; + state <= `S_WC; + cw_dir <= ~cw_dir; + ack_exp_adr <= wb_exp_adr; + end + end + `S_DATA_WT: begin + cw_req <= 1'b0; + wb_ack <= 1'b0; + wb_err <= 1'b0; + // wait for new data and send sync signal + // continous data delivery with clock sychronizers is not possible, due to ACK delay + // it causes no problem with read burst, beacause wishbone communication is unidirectional then + if (wb_cyc & wb_stb & ~wb_ack) begin // wait for stb reassert when new burst data is delivered + cw_req <= 1'b1; + cw_io_o <= wb_o_dat; + state <= `S_DATA_W; + end + end + `S_WC: begin + cw_dir <= 1'b0; + cw_req <= 1'b0; + wb_ack <= 1'b0; + wb_err <= 1'b0; + state <= `S_IDLE; + end + endcase + end +end + +`undef S_DATA_R +`undef S_DATA_W + +endmodule
diff --git a/verilog/rtl/ppcpu/rtl/interconnect/outer/wb_cross_clk.v b/verilog/rtl/ppcpu/rtl/interconnect/outer/wb_cross_clk.v new file mode 100644 index 0000000..5dd8326 --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/interconnect/outer/wb_cross_clk.v
@@ -0,0 +1,180 @@ +`include "config.v" + +module wb_cross_clk ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + input clk_m, + input clk_s, + input m_rst, + input s_rst, + + input m_wb_cyc, + input m_wb_stb, + input [`WB_ADDR_W-1:0] m_wb_adr, + input [`RW-1:0] m_wb_o_dat, + output [`RW-1:0] m_wb_i_dat, + input m_wb_we, + input [1:0] m_wb_sel, + input m_wb_8_burst, m_wb_4_burst, + output m_wb_ack, + output m_wb_err, + + output s_wb_cyc, + output s_wb_stb, + output [`WB_ADDR_W-1:0] s_wb_adr, + output [`RW-1:0] s_wb_o_dat, + input [`RW-1:0] s_wb_i_dat, + output s_wb_we, + output [1:0] s_wb_sel, + output s_wb_8_burst, s_wb_4_burst, + input s_wb_ack, + input s_wb_err +); + +reg prev_stb, prev_ack; +always @(posedge clk_m) begin + if (m_rst) begin + prev_stb <= 1'b0; + prev_ack <= 1'b0; + end else begin + prev_stb <= m_wb_stb; + prev_ack <= (msy_flag_ack | msy_flag_err); + end +end +wire m_new_req = m_wb_stb & (~prev_stb | prev_ack) & ~ignored_addr; +wire ignored_addr = m_wb_adr < `WB_ADDR_W'h002000; + +`define MAX_BURST_LOG 4 +reg [`MAX_BURST_LOG-1:0] m_burst_cnt; + +reg m_new_req_flag; +always @(posedge clk_m) begin + if (m_rst) begin + m_new_req_flag <= 1'b0; + m_burst_cnt <= `MAX_BURST_LOG'b0; + end else if (m_new_req & ~(|m_burst_cnt)) begin + m_new_req_flag <= ~m_new_req_flag; + if (m_wb_we) + m_burst_cnt <= `MAX_BURST_LOG'd1; // burst write is not possible, as new data must be transmitted after each ack, which causes delay + // although burst signal is passed for later optimization of compressed bus write + else + m_burst_cnt <= (m_wb_8_burst ? `MAX_BURST_LOG'd8 : (m_wb_4_burst ? `MAX_BURST_LOG'd4 : `MAX_BURST_LOG'd1)); + end else if (msy_flag_ack | msy_flag_err) begin + m_burst_cnt <= m_burst_cnt - `MAX_BURST_LOG'b1; + end +end + +reg ack_next_hold; +reg [`MAX_BURST_LOG-1:0] s_burst_cnt; +wire burst_in_progress = (|s_burst_cnt); + +always @(posedge clk_s) begin + if (s_rst) begin + ack_next_hold <= 1'b0; + s_burst_cnt <= `MAX_BURST_LOG'b0; + end else if ((s_wb_ack | s_wb_err) & burst_in_progress) begin + s_burst_cnt <= s_burst_cnt - `MAX_BURST_LOG'b1; + ack_next_hold <= 1'b0; + end else if ((s_wb_ack | s_wb_err) & ~burst_in_progress) begin + ack_next_hold <= 1'b1; + end else if (ssy_newreq) begin + ack_next_hold <= 1'b0; + if (m_wb_we) + s_burst_cnt <= 'b0; + else + s_burst_cnt <= (s_wb_8_burst ? `MAX_BURST_LOG'd7 : (s_wb_4_burst ? `MAX_BURST_LOG'd3 : `MAX_BURST_LOG'd0)); + end +end + +assign s_wb_stb = s_wb_cyc & ~ack_next_hold; + +// XOR strobe setup +reg ack_xor_flag; +reg err_xor_flag; +always @(posedge clk_s) begin + if(s_rst) begin + ack_xor_flag <= 1'b0; + err_xor_flag <= 1'b0; + end else begin + if(s_wb_ack) + ack_xor_flag <= ~ack_xor_flag; + if(s_wb_err) + err_xor_flag <= ~err_xor_flag; + end +end + +reg prev_xor_ack, prev_xor_err; +always @(posedge clk_m) begin + if(m_rst) begin + prev_xor_ack <= 1'b0; + prev_xor_err <= 1'b0; + end else begin + prev_xor_ack <= msy_xor_ack; + prev_xor_err <= msy_xor_err; + end +end +wire msy_flag_ack = prev_xor_ack ^ msy_xor_ack; +wire msy_flag_err = prev_xor_err ^ msy_xor_err; + +reg prev_xor_newreq; +always @(posedge clk_s) begin + if(s_rst) + prev_xor_newreq <= 1'b0; + else + prev_xor_newreq <= ssy_flag_newreq; +end +wire ssy_newreq = prev_xor_newreq ^ ssy_flag_newreq; + +// S->M ff sync +`define SM_SYNC_W 2+16 +wire [`SM_SYNC_W-1:0] smsync1; +ff_mb_sync #(.DATA_W(`SM_SYNC_W)) s_m_sync ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .src_clk(clk_s), + .dst_clk(clk_m), + .src_rst(s_rst), + .dst_rst(m_rst), + .i_data({s_wb_i_dat, err_xor_flag^s_wb_err, ack_xor_flag^s_wb_ack}), + .o_data(smsync1), + .i_xfer_req(s_wb_ack | s_wb_err) // NOTE: CLOCK MUST BE DIVIDED BY >4 TO NOT VIOLATE 3 CYCLE DELAY (to dst clock) +); + +assign m_wb_i_dat = smsync1[17:2]; +assign m_wb_ack = msy_flag_ack; +assign m_wb_err = msy_flag_err; + +wire msy_xor_ack = smsync1[0]; +wire msy_xor_err = smsync1[1]; + +// M->S ff sync +`define MS_SYNC_W 1+24+16+1+2+2+1 +wire [`MS_SYNC_W-1:0] mssync1; + +ff_mb_sync #(.DATA_W(`MS_SYNC_W)) m_s_sync ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .src_clk(clk_m), + .dst_clk(clk_s), + .src_rst(m_rst), + .dst_rst(s_rst), + .i_data({m_wb_cyc, m_wb_adr, m_wb_o_dat, m_wb_we, m_wb_sel, m_wb_4_burst, m_wb_8_burst, ~m_new_req_flag}), + .o_data(mssync1), + .i_xfer_req(m_new_req & ~(|m_burst_cnt)) +); + +wire ssy_flag_newreq = mssync1[0]; +assign s_wb_8_burst = mssync1[1]; +assign s_wb_4_burst = mssync1[2]; +assign s_wb_sel = mssync1[4:3]; +assign s_wb_we = mssync1[5]; +assign s_wb_o_dat = mssync1[21:6]; +assign s_wb_adr = mssync1[45:22]; +assign s_wb_cyc = mssync1[46]; + +endmodule
diff --git a/verilog/rtl/ppcpu/rtl/interconnect/outer/wb_decomp.v b/verilog/rtl/ppcpu/rtl/interconnect/outer/wb_decomp.v new file mode 100644 index 0000000..2a7531b --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/interconnect/outer/wb_decomp.v
@@ -0,0 +1,127 @@ +`include "config.v" + +module wb_decomp ( + input i_clk, + input i_rst, + + output reg wb_cyc, + output reg wb_stb, + output [`WB_ADDR_W-1:0] wb_adr, + output reg [`RW-1:0] wb_o_dat, + input [`RW-1:0] wb_i_dat, + output reg wb_we, + output reg [1:0] wb_sel, + input wb_ack, + input wb_err, + + input [`RW-1:0] cw_io_i, + output reg [`RW-1:0] cw_io_o, + input cw_req, + input cw_dir, + output reg cw_ack, + output reg cw_err +); + +`define SW 4 +`define S_IDLE `SW'b0 +`define S_HDR_1 `SW'b1 +`define S_DATA_R `SW'b10 +`define S_DATA_W `SW'b11 +`define S_DATA_W_PRE `SW'b100 +reg [`SW-1:0] state; + +reg [`WB_ADDR_W-1:0] l_wb_adr; +assign wb_adr = l_wb_adr + {20'b0, burst_cnt}; + +`define MAX_BRST_LOG 3 +reg [`MAX_BRST_LOG-1:0] burst_end, burst_cnt; + +`define DATA_WRITE_DELAY 3 +`define DATA_WDEL_LOG 2 +reg [`DATA_WDEL_LOG-1:0] wdel_cnt; + +always @(posedge i_clk) begin + if (i_rst) begin + state <= `S_IDLE; + cw_ack <= 1'b0; + wb_cyc <= 1'b0; + wb_stb <= 1'b0; + wdel_cnt <= `DATA_WDEL_LOG'b0; + end else begin + case (state) + default: begin + cw_ack <= 1'b0; + cw_err <= 1'b0; + wb_cyc <= 1'b0; + wb_stb <= 1'b0; + if (cw_req & cw_io_i[0]) begin + state <= `S_HDR_1; + l_wb_adr[`WB_ADDR_W-1:`RW] <= cw_io_i[`RW-1:8]; + wb_we <= cw_io_i[3]; + wb_sel <= cw_io_i[2:1]; + burst_end <= (~(|cw_io_i[7:4]) ? 3'd0 : (cw_io_i[4] ? 3'd7 : 3'd3)); + burst_cnt <= `MAX_BRST_LOG'b0; + cw_ack <= 1'b1; + end + end + `S_HDR_1: begin + state <= (wb_we ? `S_DATA_W_PRE : `S_DATA_R); + l_wb_adr[`RW-1:0] <= cw_io_i; + cw_ack <= 1'b0; + if (~wb_we) begin + wb_cyc <= 1'b1; + wb_stb <= 1'b1; + end + end + `S_DATA_R: begin + cw_ack <= 1'b0; + cw_err <= 1'b0; + if ((wb_ack | wb_err) && burst_cnt != burst_end) begin + cw_ack <= wb_ack; + cw_err <= wb_err; + burst_cnt <= burst_cnt + `MAX_BRST_LOG'b1; + cw_io_o <= wb_i_dat; + end else if ((wb_ack | wb_err) && burst_cnt == burst_end) begin + cw_ack <= wb_ack; + cw_err <= wb_err; + wb_stb <= 1'b0; + state <= `S_IDLE; + cw_io_o <= wb_i_dat; + end + end + `S_DATA_W_PRE: begin + wb_cyc <= 1'b1; + wb_stb <= 1'b1; + wb_o_dat <= cw_io_i; + state <= `S_DATA_W; + end + `S_DATA_W: begin + if (~wb_stb & cw_req) begin // wait for write burst new data + wb_stb <= 1'b1; + wb_o_dat <= cw_io_i; + end + + cw_ack <= 1'b0; + cw_err <= 1'b0; + if ((wb_ack | wb_err) && burst_cnt != burst_end) begin + cw_ack <= wb_ack; + cw_err <= wb_err; + wb_stb <= 1'b0; + burst_cnt <= burst_cnt + `MAX_BRST_LOG'b1; + end else if ((wb_ack | wb_err) && burst_cnt == burst_end) begin + cw_ack <= wb_ack; + cw_err <= wb_err; + wb_cyc <= 1'b0; + wb_stb <= 1'b0; + state <= `S_IDLE; + end + end + endcase + end +end + +endmodule + +`undef S_DATA_R +`undef S_DATA_W +`undef SW \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/rtl/top.v b/verilog/rtl/ppcpu/rtl/top.v new file mode 100644 index 0000000..8b4420e --- /dev/null +++ b/verilog/rtl/ppcpu/rtl/top.v
@@ -0,0 +1,417 @@ +// Connects all moules to interconnects +// No logic or buffers allowed here +// See both interconnect codes to see how it is connected + +`include "config.v" + +`define MPRJ_IO_PADS 38 +`define WB_DATA_W 16 +`define WB_SEL_BITS 2 + +module top ( +`ifdef USE_POWER_PINS + inout vccd1, + inout vssd1, +`endif + + // IOs + input [`MPRJ_IO_PADS-1:0] m_io_in, + output [`MPRJ_IO_PADS-1:0] m_io_out, + output [`MPRJ_IO_PADS-1:0] m_io_oeb, + + // Wishbone from managment core + input mgt_wb_clk_i, + input mgt_wb_rst_i, + input mgt_wb_stb_i, + input mgt_wb_cyc_i, + input mgt_wb_we_i, + input [3:0] mgt_wb_sel_i, + input [31:0] mgt_wb_dat_i, + input [31:0] mgt_wb_adr_i, + output mgt_wb_ack_o, + output [31:0] mgt_wb_dat_o, + + // Logic Analyzer Signals + input [127:0] la_data_in, + output [127:0] la_data_out, + input [127:0] la_oenb, + + // IRQ + output [2:0] irq, + + // Independent clock (on independent integer divider) + input user_clock2 +); + +/* + * UPPER INTERCONNECT + * External bus, clocking, outside signals + */ +wire inner_clock, inner_reset; +interconnect_outer interconnect_outer ( + .m_io_in(m_io_in), + .m_io_out(m_io_out), + .m_io_oeb(m_io_oeb), + .mgt_wb_clk_i(mgt_wb_clk_i), + .mgt_wb_rst_i(mgt_wb_rst_i), + .mgt_wb_stb_i(mgt_wb_stb_i), + .mgt_wb_cyc_i(mgt_wb_cyc_i), + .mgt_wb_we_i(mgt_wb_we_i), + .mgt_wb_sel_i(mgt_wb_sel_i), + .mgt_wb_dat_i(mgt_wb_dat_i), + .mgt_wb_adr_i(mgt_wb_adr_i), + .mgt_wb_ack_o(mgt_wb_ack_o), + .mgt_wb_dat_o(mgt_wb_dat_o), + .la_data_in(la_data_in), + .la_data_out(la_data_out), + .la_oenb(la_oenb), + .irq(irq), + .user_clock2(user_clock2), + .inner_clock(inner_clock), + .inner_reset(inner_reset), + .inner_wb_cyc(inner_wb_cyc), + .inner_wb_stb(inner_wb_stb), + .inner_wb_we(inner_wb_we), + .inner_wb_adr(inner_wb_adr), + .inner_wb_o_dat(inner_wb_o_dat), + .inner_wb_i_dat(inner_wb_i_dat), + .inner_wb_ack(inner_wb_ack), + .inner_wb_err(inner_wb_err), + .inner_wb_sel(inner_wb_sel), + .inner_wb_4_burst(inner_wb_4_burst), + .inner_wb_8_burst(inner_wb_8_burst), + .inner_ext_irq(inner_ext_irq), + .inner_embed_mode(inner_embed_mode), + .inner_disable(inner_disable), + .iram_clk(iram_clk), + .iram_addr(iram_addr), + .iram_i_data(iram_i_data), + .iram_o_data(iram_o_data), + .iram_we(iram_we) +); + +wire inner_wb_cyc, inner_wb_stb; +wire inner_wb_we; +wire [`WB_ADDR_W-1:0] inner_wb_adr; +wire [`WB_DATA_W-1:0] inner_wb_o_dat; +wire [`WB_DATA_W-1:0] inner_wb_i_dat; +wire inner_wb_ack, inner_wb_err; +wire [`WB_SEL_BITS-1:0] inner_wb_sel; +wire inner_wb_4_burst, inner_wb_8_burst; +wire inner_ext_irq; +wire inner_embed_mode; +wire inner_disable; + +/* + * LOWER INTERCONNECT + * All cpu functions: core and caches + */ + +interconnect_inner interconnect_inner ( + .core_clock(inner_clock), + .core_reset(inner_reset), + .inner_wb_cyc(inner_wb_cyc), // why some compilers don't support .* ??? + .inner_wb_stb(inner_wb_stb), + .inner_wb_we(inner_wb_we), + .inner_wb_adr(inner_wb_adr), + .inner_wb_o_dat(inner_wb_o_dat), + .inner_wb_i_dat(inner_wb_i_dat), + .inner_wb_ack(inner_wb_ack), + .inner_wb_err(inner_wb_err), + .inner_wb_sel(inner_wb_sel), + .inner_wb_4_burst(inner_wb_4_burst), + .inner_wb_8_burst(inner_wb_8_burst), + .inner_ext_irq(inner_ext_irq), + .inner_embed_mode(inner_embed_mode), + .c0_clk(c0_clk), + .c0_rst(c0_rst), + .c0_disable(c0_disable), + .c0_o_req_addr(c0_o_req_addr), + .c0_o_req_active(c0_o_req_active), + .c0_o_req_ppl_submit(c0_o_req_ppl_submit), + .c0_i_req_data(c0_i_req_data), + .c0_i_req_data_valid(c0_i_req_data_valid), + .c0_dbg_r0(c0_dbg_r0), + .c0_dbg_pc(c0_dbg_pc), + .c0_o_mem_addr(c0_o_mem_addr), + .c0_o_mem_data(c0_o_mem_data), + .c0_i_mem_data(c0_i_mem_data), + .c0_o_mem_req(c0_o_mem_req), + .c0_o_mem_we(c0_o_mem_we), + .c0_i_mem_ack(c0_i_mem_ack), + .c0_o_mem_sel(c0_o_mem_sel), + .c0_i_irq(c0_i_irq), + .c0_o_c_instr_page(c0_o_c_instr_page), + .c0_o_c_data_page(c0_o_c_data_page), + .c0_sr_bus_addr(c0_sr_bus_addr), + .c0_sr_bus_data_o(c0_sr_bus_data_o), + .c0_sr_bus_we(c0_sr_bus_we), + .c0_o_icache_flush(c0_o_icache_flush), + .c0_i_mem_exception(c0_i_mem_exception), + .c0_i_mc_core_int(c0_i_mc_core_int), + .c0_i_core_int_sreg(c0_i_core_int_sreg), + .c0_o_c_instr_long(c0_o_c_instr_long), + .c0_o_instr_long_addr(c0_o_instr_long_addr), + .c0_o_mem_long_mode(c0_o_mem_long), + .c0_o_mem_high_addr(c0_o_mem_addr_high), + .c1_clk(c1_clk), + .c1_rst(c1_rst), + .c1_disable(c1_disable), + .c1_o_req_addr(c1_o_req_addr), + .c1_o_req_active(c1_o_req_active), + .c1_o_req_ppl_submit(c1_o_req_ppl_submit), + .c1_i_req_data(c1_i_req_data), + .c1_i_req_data_valid(c1_i_req_data_valid), + .c1_dbg_r0(c1_dbg_r0), + .c1_dbg_pc(c1_dbg_pc), + .c1_o_mem_addr(c1_o_mem_addr), + .c1_o_mem_data(c1_o_mem_data), + .c1_i_mem_data(c1_i_mem_data), + .c1_o_mem_req(c1_o_mem_req), + .c1_o_mem_we(c1_o_mem_we), + .c1_i_mem_ack(c1_i_mem_ack), + .c1_o_mem_sel(c1_o_mem_sel), + .c1_i_irq(c1_i_irq), + .c1_o_c_instr_page(c1_o_c_instr_page), + .c1_o_c_data_page(c1_o_c_data_page), + .c1_sr_bus_addr(c1_sr_bus_addr), + .c1_sr_bus_data_o(c1_sr_bus_data_o), + .c1_sr_bus_we(c1_sr_bus_we), + .c1_o_icache_flush(c1_o_icache_flush), + .c1_i_mem_exception(c1_i_mem_exception), + .c1_i_mc_core_int(c1_i_mc_core_int), + .c1_i_core_int_sreg(c1_i_core_int_sreg), + .c1_o_c_instr_long(c1_o_c_instr_long), + .c1_o_instr_long_addr(c1_o_instr_long_addr), + .c1_o_mem_long_mode(c1_o_mem_long), + .c1_o_mem_high_addr(c1_o_mem_addr_high), + .ic0_clk(ic0_clk), + .ic0_rst(ic0_rst), + .ic0_mem_req(ic0_mem_req), + .ic0_mem_ack(ic0_mem_ack), + .ic0_mem_addr(ic0_mem_addr), + .ic0_mem_data(ic0_mem_data), + .ic0_mem_ppl_submit(ic0_mem_ppl_submit), + .ic0_mem_cache_flush(ic0_mem_cache_flush), + .ic0_wb_cyc(ic0_wb_cyc), + .ic0_wb_stb(ic0_wb_stb), + .ic0_wb_i_dat(ic0_wb_i_dat), + .ic0_wb_adr(ic0_wb_adr), + .ic0_wb_we(ic0_wb_we), + .ic0_wb_ack(ic0_wb_ack), + .ic0_wb_sel(ic0_wb_sel), + .ic0_wb_err(ic0_wb_err), + .ic1_clk(ic1_clk), + .ic1_rst(ic1_rst), + .ic1_mem_req(ic1_mem_req), + .ic1_mem_ack(ic1_mem_ack), + .ic1_mem_addr(ic1_mem_addr), + .ic1_mem_data(ic1_mem_data), + .ic1_mem_ppl_submit(ic1_mem_ppl_submit), + .ic1_mem_cache_flush(ic1_mem_cache_flush), + .ic1_wb_cyc(ic1_wb_cyc), + .ic1_wb_stb(ic1_wb_stb), + .ic1_wb_i_dat(ic1_wb_i_dat), + .ic1_wb_adr(ic1_wb_adr), + .ic1_wb_we(ic1_wb_we), + .ic1_wb_ack(ic1_wb_ack), + .ic1_wb_sel(ic1_wb_sel), + .ic1_wb_err(ic1_wb_err), + .dcache_clk(dcache_clk), + .dcache_rst(dcache_rst), + .dcache_mem_req(dcache_mem_req), + .dcache_mem_we(dcache_mem_we), + .dcache_mem_ack(dcache_mem_ack), + .dcache_mem_addr(dcache_mem_addr), + .dcache_mem_i_data(dcache_mem_i_data), + .dcache_mem_o_data(dcache_mem_o_data), + .dcache_mem_sel(dcache_mem_sel), + .dcache_mem_cache_enable(dcache_mem_cache_enable), + .dcache_mem_exception(dcache_mem_exception), + .dcache_wb_cyc(dcache_wb_cyc), + .dcache_wb_stb(dcache_wb_stb), + .dcache_wb_i_dat(dcache_wb_i_dat), + .dcache_wb_o_dat(dcache_wb_o_dat), + .dcache_wb_adr(dcache_wb_adr), + .dcache_wb_we(dcache_wb_we), + .dcache_wb_sel(dcache_wb_sel), + .dcache_wb_4_burst(dcache_wb_4_burst), + .dcache_wb_ack(dcache_wb_ack), + .dcache_wb_err(dcache_wb_err), + .inner_disable(inner_disable) +); + +// CORES + +wire c0_clk, c0_rst; +wire [`RW-1:0] c0_o_req_addr; +wire c0_o_req_active; +wire [`I_SIZE-1:0] c0_i_req_data; +wire c0_i_req_data_valid; +wire c0_o_req_ppl_submit; +wire [`RW-1:0] c0_o_mem_addr, c0_o_mem_data, c0_i_mem_data; +wire c0_o_mem_req, c0_o_mem_we, c0_i_mem_ack; +wire [`ADDR_BYTES-1:0] c0_o_mem_sel; +wire [`RW-1:0] c0_sr_bus_addr, c0_sr_bus_data_o, c0_i_core_int_sreg; +wire c0_sr_bus_we; +wire c0_o_c_instr_page, c0_o_c_data_page, c0_o_icache_flush, c0_i_mem_exception; +wire [`RW-1:0] c0_dbg_r0, c0_dbg_pc; +wire c0_i_mc_core_int, c0_disable, c0_i_irq; +wire c0_o_c_instr_long; +wire [7:0] c0_o_instr_long_addr; +wire c0_o_mem_long; +wire [7:0] c0_o_mem_addr_high; + +core0 core0 ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(c0_clk), .i_rst(c0_rst), .o_req_addr(c0_o_req_addr), .o_req_active(c0_o_req_active), .i_req_data(c0_i_req_data), .i_req_data_valid(c0_i_req_data_valid), .o_req_ppl_submit(c0_o_req_ppl_submit), + .o_mem_addr(c0_o_mem_addr), .o_mem_data(c0_o_mem_data), .i_mem_data(c0_i_mem_data), .o_mem_req(c0_o_mem_req), .o_mem_we(c0_o_mem_we), .i_mem_ack(c0_i_mem_ack), + .dbg_r0(c0_dbg_r0), .dbg_pc(c0_dbg_pc), .i_irq(c0_i_irq), .o_c_instr_page(c0_o_c_instr_page), .sr_bus_addr(c0_sr_bus_addr), + .sr_bus_data_o(c0_sr_bus_data_o), .sr_bus_we(c0_sr_bus_we), .o_icache_flush(c0_o_icache_flush), .o_mem_sel(c0_o_mem_sel), .o_c_data_page(c0_o_c_data_page), + .i_mem_exception(c0_i_mem_exception), .i_disable(c0_disable), .i_mc_core_int(c0_i_mc_core_int), .i_core_int_sreg(c0_i_core_int_sreg), + .o_c_instr_long(c0_o_c_instr_long), .o_instr_long_addr(c0_o_instr_long_addr), .o_mem_long(c0_o_mem_long), .o_mem_addr_high(c0_o_mem_addr_high) +); + +wire c1_clk, c1_rst; +wire [`RW-1:0] c1_o_req_addr; +wire c1_o_req_active; +wire [`I_SIZE-1:0] c1_i_req_data; +wire c1_i_req_data_valid; +wire c1_o_req_ppl_submit; +wire [`RW-1:0] c1_o_mem_addr, c1_o_mem_data, c1_i_mem_data; +wire c1_o_mem_req, c1_o_mem_we, c1_i_mem_ack; +wire [`ADDR_BYTES-1:0] c1_o_mem_sel; +wire [`RW-1:0] c1_sr_bus_addr, c1_sr_bus_data_o, c1_i_core_int_sreg; +wire c1_sr_bus_we; +wire c1_o_c_instr_page, c1_o_c_data_page, c1_o_icache_flush, c1_i_mem_exception; +wire [`RW-1:0] c1_dbg_r0, c1_dbg_pc; +wire c1_i_mc_core_int, c1_disable, c1_i_irq; +wire c1_o_c_instr_long; +wire [7:0] c1_o_instr_long_addr; +wire c1_o_mem_long; +wire [7:0] c1_o_mem_addr_high; + +core1 core1 ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(c1_clk), .i_rst(c1_rst), .o_req_addr(c1_o_req_addr), .o_req_active(c1_o_req_active), .i_req_data(c1_i_req_data), .i_req_data_valid(c1_i_req_data_valid), .o_req_ppl_submit(c1_o_req_ppl_submit), + .o_mem_addr(c1_o_mem_addr), .o_mem_data(c1_o_mem_data), .i_mem_data(c1_i_mem_data), .o_mem_req(c1_o_mem_req), .o_mem_we(c1_o_mem_we), .i_mem_ack(c1_i_mem_ack), + .dbg_r0(c1_dbg_r0), .dbg_pc(c1_dbg_pc), .i_irq(c1_i_irq), .o_c_instr_page(c1_o_c_instr_page), .sr_bus_addr(c1_sr_bus_addr), + .sr_bus_data_o(c1_sr_bus_data_o), .sr_bus_we(c1_sr_bus_we), .o_icache_flush(c1_o_icache_flush), .o_mem_sel(c1_o_mem_sel), .o_c_data_page(c1_o_c_data_page), + .i_mem_exception(c1_i_mem_exception), .i_disable(c1_disable), .i_mc_core_int(c1_i_mc_core_int), .i_core_int_sreg(c1_i_core_int_sreg), + .o_c_instr_long(c1_o_c_instr_long), .o_instr_long_addr(c1_o_instr_long_addr), .o_mem_long(c1_o_mem_long), .o_mem_addr_high(c1_o_mem_addr_high) +); + +// DCACHE + +wire dcache_clk; +wire dcache_rst; +wire dcache_mem_req; +wire dcache_mem_we; +wire dcache_mem_ack; +wire [`WB_ADDR_W-1:0] dcache_mem_addr; +wire [`RW-1:0] dcache_mem_i_data; +wire [`RW-1:0] dcache_mem_o_data; +wire [1:0] dcache_mem_sel; +wire dcache_mem_cache_enable; +wire dcache_mem_exception; +wire dcache_wb_cyc; +wire dcache_wb_stb; +wire [`RW-1:0] dcache_wb_i_dat; +wire [`RW-1:0] dcache_wb_o_dat; +wire [`WB_ADDR_W-1:0] dcache_wb_adr; +wire dcache_wb_we; +wire [1:0] dcache_wb_sel; +wire dcache_wb_4_burst; +wire dcache_wb_ack; +wire dcache_wb_err; + +dcache dcache ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(dcache_clk), .i_rst(dcache_rst), .mem_req(dcache_mem_req), .mem_addr(dcache_mem_addr), .mem_i_data(dcache_mem_i_data), .mem_o_data(dcache_mem_o_data), .mem_we(dcache_mem_we), .mem_ack(dcache_mem_ack), .mem_sel(dcache_mem_sel), + .wb_cyc(dcache_wb_cyc), .wb_stb(dcache_wb_stb), .wb_we(dcache_wb_we), .wb_ack(dcache_wb_ack), .wb_i_dat(dcache_wb_i_dat), .wb_o_dat(dcache_wb_o_dat), .wb_adr(dcache_wb_adr), .wb_sel(dcache_wb_sel), .mem_cache_enable(dcache_mem_cache_enable), .wb_4_burst(dcache_wb_4_burst), .wb_err(dcache_wb_err), .mem_exception(dcache_mem_exception) +); + +// ICACHES + +wire ic0_clk, ic0_rst; +wire ic0_mem_req; +wire ic0_mem_ack; +wire [`RW-1:0] ic0_mem_addr; +wire [`I_SIZE-1:0] ic0_mem_data; +wire ic0_mem_ppl_submit; +wire ic0_mem_cache_flush; +wire ic0_wb_cyc; +wire ic0_wb_stb; +wire [`RW-1:0] ic0_wb_i_dat; +wire [`RW-1:0] ic0_wb_adr; +wire ic0_wb_we; +wire ic0_wb_ack; +wire [1:0] ic0_wb_sel; +wire ic0_wb_err; + +icache icache_0 ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(ic0_clk), .i_rst(ic0_rst), .mem_req(ic0_mem_req), .mem_addr(ic0_mem_addr), .mem_data(ic0_mem_data), .mem_ack(ic0_mem_ack), .mem_ppl_submit(ic0_mem_ppl_submit), + .wb_cyc(ic0_wb_cyc), .wb_stb(ic0_wb_stb), .wb_we(ic0_wb_we), .wb_ack(ic0_wb_ack), .wb_i_dat(ic0_wb_i_dat), .wb_adr(ic0_wb_adr), .wb_sel(ic0_wb_sel), .mem_cache_flush(ic0_mem_cache_flush), .wb_err(ic0_wb_err) +); + +wire ic1_clk, ic1_rst; +wire ic1_mem_req; +wire ic1_mem_ack; +wire [`RW-1:0] ic1_mem_addr; +wire [`I_SIZE-1:0] ic1_mem_data; +wire ic1_mem_ppl_submit; +wire ic1_mem_cache_flush; +wire ic1_wb_cyc; +wire ic1_wb_stb; +wire [`RW-1:0] ic1_wb_i_dat; +wire [`RW-1:0] ic1_wb_adr; +wire ic1_wb_we; +wire ic1_wb_ack; +wire [1:0] ic1_wb_sel; +wire ic1_wb_err; + +icache icache_1 ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), .vssd1(vssd1), +`endif + .i_clk(ic1_clk), .i_rst(ic1_rst), .mem_req(ic1_mem_req), .mem_addr(ic1_mem_addr), .mem_data(ic1_mem_data), .mem_ack(ic1_mem_ack), .mem_ppl_submit(ic1_mem_ppl_submit), + .wb_cyc(ic1_wb_cyc), .wb_stb(ic1_wb_stb), .wb_we(ic1_wb_we), .wb_ack(ic1_wb_ack), .wb_i_dat(ic1_wb_i_dat), .wb_adr(ic1_wb_adr), .wb_sel(ic1_wb_sel), .mem_cache_flush(ic1_mem_cache_flush), .wb_err(ic1_wb_err) +); + + +// Internal ram +wire iram_clk, iram1_clk; +wire [7:0] iram_addr, iram1_addr; +wire [31:0] iram_i_data, iram_o_data, iram1_dout; +wire [3:0] iram_w_mask; +wire iram_we, iram_csb, iram1_csb; + +sky130_sram_1kbyte_1rw1r_32x256_8 int_ram ( +`ifdef USE_POWER_PINS + .vccd1(vccd1), + .vssd1(vssd1), +`endif + .clk0(iram_clk), + .csb0(iram_csb), + .web0(iram_we), + .wmask0(iram_w_mask), + .addr0(iram_addr), + .din0(iram_i_data), + .dout0(iram_o_data), + .clk1(iram1_clk), + .csb1(iram1_csb), + .addr1(iram1_addr), + .dout1(iram1_dout) +); + + +endmodule
diff --git a/verilog/rtl/ppcpu/soc/bootloader.s b/verilog/rtl/ppcpu/soc/bootloader.s new file mode 100644 index 0000000..ef9340a --- /dev/null +++ b/verilog/rtl/ppcpu/soc/bootloader.s
@@ -0,0 +1,189 @@ +start: + ldi r0, 0b011 ; enable data paging + srs r0, 1 + ldi r0, 0x004 + srs r0, 0x201 ; map page 1 to device address + + ; ldi r0, 0x1000 + ; srs r0, 0x200 + + ; ldd r0, 0 + ; jal r6, putc + ; sri r0, r0, 8 + ; jal r6, putc + + ; ; static test + ; ldi r0, 0x0000 + ; std r0, 0x0 + ; ldi r0, 0x0000 + ; std r0, 0x2 + ; ldi r0, 0x0e00 + ; std r0, 0x4 + ; ldi r0, 0 + ; std r0, 6 + ; ldi r0, 0x0e + ; std r0, 0x8 + ; ldi r0, 0 + ; std r0, 0xa + ; srs r0, 2 + ; jmp start + + ; ldi r0, 0x000e + ; std r0, 0x0 + ; ldi r0, 0x0000 + ; std r0, 0x2 + + ; ldi r0, 0x000e + ; std r0, 0x4 + ; ldi r0, 0x0000 + ; std r0, 0x6 + + jal r6, print_welcome + + main_loop: + ldi r0, 0x3e + jal r6, putc + + jal r6, getc + + cmi r0, 0x70 + jeq dump_page + + cmi r0, 0x65 + jeq exec + + cmi r0, 0x6d + jeq load_data_mem + + jmp main_loop + +exec: + ldi r0, 0b001 ; disable data paging + srs r0, 1 + + ; disable program paging and jump to true 0 + ldi r0, 0 + srs r0, 2 + jmp start + +load_data_mem: + ; read data size 0x0000 + jal r6, getc16 + mov r7, r0 + + ; read start address (in 0xppPXXX) + jal r6, getc16 + sri r3, r0, 12 ; page number + ani r5, r0, 0x0fff ; local page addr + + jal r6, getc16 + sli r0, r0, 4 + orr r3, r3, r0 ; upper page number + + ; setup page0 for selected page load + srs r3, 0x200 + + mov r0, r5 + jal r6, putc + + ldi r1, 0 ; checksum + + load_loop: + jal r6, getc16 + sto r0, r5, 0 + add r1, r1, r0s + + adi r5, r5, 2 + cmi r5, 0x1000 + jne page_update_skip + + adi r3, r3, 1 + srs r3, 0x200 + ldi r5, 0 + + page_update_skip: + adi r7, r7, -2 + cmi r7, 0 + + jca load_loop_end ; jgt unsigned + jeq load_loop_end + jmp load_loop + load_loop_end: + mov r0, r1 + jal r6, putc + sri r0, r0, 8 + jal r6, putc + jmp main_loop + +dump_page: + jal r6, getc16 + srs r0, 0x200 + + ldi r3, 0 + + dump_loop: + ldo r0, r3, 0 + jal r6, putc + sri r0, r0, 8 + jal r6, putc + + ;adi r3, r3, 1 + adi r3, r3, 2 + cmi r3, 0xffe + jle dump_loop + jmp main_loop + +putc: + putc_wait_ready: + ldd r1, 0x1000 ; temporary serial device address + cai r1, 0x2 + jeq putc_wait_ready + std r0, 0x1004 + srs r6, 0 + +getc16: + mov r2, r6 + jal r6, getc + mov r4, r0 + jal r6, getc + sli r0, r0, 8 + orr r0, r4, r0 + srs r2, 0 + +getc: + getc_wait_ready: + ldd r0, 0x1000 + cai r0, 0x1 + jeq getc_wait_ready + ldd r0, 0x1002 + srs r6, 0 + +print_welcome: + mov r5, r6 + ldi r0, 0x70 + jal r6, putc + ldi r0, 0x70 + jal r6, putc + ldi r0, 0x63 + jal r6, putc + ldi r0, 0x70 + jal r6, putc + ldi r0, 0x75 + jal r6, putc + ldi r0, 0x20 + jal r6, putc + ldi r0, 0x62 + jal r6, putc + ldi r0, 0x6f + jal r6, putc + ldi r0, 0x6f + jal r6, putc + ldi r0, 0x74 + jal r6, putc + ldi r0, 0x6c + jal r6, putc + ldi r0, 0x64 + jal r6, putc + ldi r0, 0x0a + jal r6, putc + srs r5, 0 \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/soc/irq_ctrl.v b/verilog/rtl/ppcpu/soc/irq_ctrl.v new file mode 100644 index 0000000..b232d67 --- /dev/null +++ b/verilog/rtl/ppcpu/soc/irq_ctrl.v
@@ -0,0 +1,50 @@ +// Interrrupt controller +// Takes multiple edge triggered interrupts as inputs (each able to clear and disable) +// and outputs one master level interrupt + +module irq_ctrl ( + input i_clk, + input i_rst, + + output o_irq, + + input [15:0] i_irq, + + input wb_cyc, wb_stb, wb_we, + output wb_ack, + input [23:0] wb_adr, + input [15:0] wb_i_dat, + output [15:0] wb_o_dat +); + +assign o_irq = |(irq_active & irq_mask); + +reg [15:0] irq_active; +reg [15:0] irq_mask; + +always @(posedge i_clk) begin + if (i_rst) begin + irq_active = 16'b0; + irq_mask = 16'h0000; + end else begin + if (wb_cyc & wb_stb & wb_we & wb_adr == 24'b1) + irq_active = irq_active & ~wb_i_dat; + if (wb_cyc & wb_stb & wb_we & wb_adr == 24'b10) + irq_mask = wb_i_dat; + + irq_active = irq_active | ((prev_i_irq ^ i_irq) & i_irq); + end +end + +reg [15:0] prev_i_irq; +always @(posedge i_clk) begin + if (i_rst) + prev_i_irq <= 16'b0; + else + prev_i_irq <= i_irq; +end + +assign wb_ack = wb_cyc & wb_stb; +assign wb_o_dat = (wb_adr == 24'b10 ? irq_mask : irq_active & irq_mask); + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/soc/sdram.v b/verilog/rtl/ppcpu/soc/sdram.v new file mode 100644 index 0000000..fe2c518 --- /dev/null +++ b/verilog/rtl/ppcpu/soc/sdram.v
@@ -0,0 +1,237 @@ +// SDRAM driver from pcpu repo + +module sdram ( + input wire clk, + // CPU + input wire [23:0] c_addr, + input wire [15:0] c_data_in, + input wire [1:0] c_addr_sel, + output reg [31:0] c_data_out, + input wire c_read_req, c_write_req, + output reg c_busy, c_read_ready, c_cack, + // SDRAM + output reg dr_dqml, dr_dqmh, + output wire dr_cs_n, dr_cas_n, dr_ras_n, dr_we_n, dr_cke, + output reg [1:0] dr_ba, + output reg [12:0] dr_a, + inout [15:0] dr_dq, + input wire srclk +); + +localparam CMD_NOP = 3'b111; +localparam CMD_ACTIVE = 3'b011; +localparam CMD_READ = 3'b101; +localparam CMD_WRITE = 3'b100; +localparam CMD_PRECH = 3'b010; +localparam CMD_AREFR = 3'b001; +localparam CMD_LREG = 3'b000; + +reg [2:0] ram_cmd = CMD_NOP; +assign {dr_ras_n, dr_cas_n, dr_we_n} = ram_cmd; +assign dr_cke = 1'b1; +assign dr_cs_n = 1'b0; + +localparam STATE_INIT_BEGIN = 4'b0000; +localparam STATE_INIT_PRECALL = 4'b0001; +localparam STATE_INIT_AUTOREF1 = 4'b0010; +localparam STATE_INIT_AUTOREF2 = 4'b0011; +localparam STATE_INIT_REGPROG = 4'b0100; +localparam STATE_IDLE = 4'b0101; +localparam STATE_REFR = 4'b0110; +localparam STATE_READ = 4'b0111; +localparam STATE_CASREAD = 4'b1000; +localparam STATE_WRITE = 4'b1001; +localparam STATE_WAIT = 4'b1111; +reg [3:0] state = STATE_INIT_BEGIN; + +reg [15:0] wait_reg; +reg [3:0] wait_next_state; + +reg [15:0] dr_dq_reg; +reg dr_dq_oe = 1'b0; +assign dr_dq = (dr_dq_oe ? dr_dq_reg : 16'bz); + +// refr 7.183 us -> 355 clock -8/10/15 PRECHARGE ALL BEFORE RESET +reg [8:0] autorefr_cnt = 9'd55; + +initial c_busy = 1'b1; +initial c_read_ready = 1'b0; + +reg [23:0] i_addr; +reg [15:0] i_data_in; +reg [1:0] i_wr_mask; + +reg [23:0] c_r_addr, c_w_addr; // to save addr at edge of bus + +reg sync_xory_read = 1'b0; +always @(posedge srclk) begin + if(c_read_req & ~c_busy) begin + sync_xory_read <= sync_xory_read^1; + c_r_addr <= c_addr; + end +end +reg sync_x_read_done = 1'b0; +wire pulse_c_read = (sync_xory_read ^ sync_x_read_done) & ~c_read_ready; + +reg sync_xory_write = 1'b0; +always @(posedge srclk) begin + if(c_write_req & ~c_busy) begin + sync_xory_write <= sync_xory_write^1; + c_w_addr <= c_addr; + end +end + +always @(posedge srclk) begin + c_cack <= 1'b0; + if((c_read_req & ~c_busy) | (c_write_req & ~c_busy)) begin + c_cack <= 1'b1; + end +end +reg sync_x_write_done = 1'b0; +wire pulse_c_write = sync_xory_write ^ sync_x_write_done; + +reg prev_srclk = 1'b0; +// 50 Mhz -> 20 ns +// RP 18ns RFC 60ns +always @(posedge clk) begin + //if (state != STATE_WAIT) begin + {dr_dqml, dr_dqmh} <= 2'b00; dr_dq_oe <= 1'b0; dr_a <= 13'b0; dr_ba <= 2'b0; + //end + + //prev_srclk <= srclk; + // if((prev_srclk ^ srclk) & srclk) + c_read_ready <= 1'b0; + + case (state) + STATE_INIT_BEGIN: begin + ram_cmd <= CMD_NOP; + state <= STATE_WAIT; + wait_next_state <= STATE_INIT_PRECALL; + //wait_reg <= 16'd5000; + wait_reg <= 16'd10; + end + STATE_INIT_PRECALL: begin + ram_cmd <= CMD_PRECH; + dr_a[10] <= 1'b1; // all banks + state <= STATE_WAIT; + wait_next_state <= STATE_INIT_AUTOREF1; + wait_reg <= 16'd1; + end + STATE_INIT_AUTOREF1: begin + ram_cmd <= CMD_AREFR; + state <= STATE_WAIT; + wait_next_state <= STATE_INIT_AUTOREF2; + wait_reg <= 16'd4; // 80 ns + end + STATE_INIT_AUTOREF2: begin + ram_cmd <= CMD_AREFR; + state <= STATE_WAIT; + wait_next_state <= STATE_INIT_REGPROG; + wait_reg <= 16'd4; // 80 ns + end + STATE_INIT_REGPROG: begin + ram_cmd <= CMD_LREG; + dr_a <= 13'b0001000100000; + //dr_a <= 13'b0001000010000; + // CAS 2 BURST R1 W1 SEQ + // CAS 1 CORRECT? DATASHEET + dr_ba <= 2'b00; + state <= STATE_WAIT; + wait_next_state <= STATE_IDLE; + wait_reg <= 16'd4; // 80 ns + end + STATE_IDLE: begin + // c_busy <= 1'b1; //default if not staying in idle + if(c_read_req & ~c_busy/*pulse_c_read*/) begin + ram_cmd <= CMD_ACTIVE; //CHECK IF NOT ACTIVATED ALREADY + //STORE PROG AND DATA IN DIFFERENT BANKS TO NOT PRECHARGE EVERY COMMAND + //8192 rows x 512 col x 16 bit x 4 banks + // 13 b + 9 b + 16 b + 2b + i_addr <= c_addr; + i_data_in <= c_data_in; + dr_ba <= {c_addr[23:22]}; // lower max ram addr and set banks to msb??? + dr_a <= c_addr[21:9]; + state <= STATE_WAIT; + wait_next_state <= STATE_READ; + wait_reg <= 16'd1; + c_busy <= 1'b1; + sync_x_read_done <= sync_x_read_done^1; + end else if(c_write_req & ~c_busy/*pulse_c_write*/) begin + ram_cmd <= CMD_ACTIVE; + i_addr <= c_addr; + i_data_in <= c_data_in; + i_wr_mask <= ~c_addr_sel; + dr_ba <= {c_addr[23:22]}; + dr_a <= c_addr[21:9]; + state <= STATE_WAIT; + wait_next_state <= STATE_WRITE; + wait_reg <= 16'd1; + c_busy <= 1'b1; + sync_x_write_done <= sync_x_write_done^1; + end else if(~(|autorefr_cnt)) begin + ram_cmd <= CMD_PRECH; + dr_a[10] <= 1'b1; //ALL BANKS + state <= STATE_WAIT; + wait_next_state <= STATE_REFR; + wait_reg <= 16'd1; + c_busy <= 1'b1; + end else begin + ram_cmd <= CMD_NOP; + state <= STATE_IDLE; + c_busy <= 1'b0; + end + end + STATE_WRITE: begin + ram_cmd <= CMD_WRITE; + {dr_dqmh, dr_dqml} <= i_wr_mask; + dr_ba <= {i_addr[23:22]}; + dr_a[8:0] <= i_addr[8:0]; + dr_a[9] <= 1'b0; dr_a[12:11] <= 2'b0; + dr_a[10] <= 1'b1; //auto precharge + dr_dq_reg <= i_data_in; + dr_dq_oe <= 1'b1; + state <= STATE_WAIT; + wait_next_state <= STATE_IDLE; + wait_reg <= 16'd1; + end + STATE_REFR: begin + ram_cmd <= CMD_AREFR; + state <= STATE_WAIT; + wait_next_state <= STATE_IDLE; + wait_reg <= 16'd4; + autorefr_cnt <= 9'd355; + end + STATE_READ: begin + ram_cmd <= CMD_READ; + {dr_dqml, dr_dqmh} <= 2'b00; + //don't use burst but two subseq reads for instr + dr_ba <= {i_addr[23:22]}; + dr_a[8:0] <= i_addr[8:0]; + dr_a[9] <= 1'b0; dr_a[12:11] <= 2'b0; + dr_a[10] <= 1'b1; //auto precharge + state <= STATE_WAIT; + wait_next_state <= STATE_CASREAD; + wait_reg <= 16'd1; + end + STATE_CASREAD: begin + state <= STATE_IDLE; //no wait needed + ram_cmd <= CMD_NOP; + c_data_out <= {16'b0, dr_dq}; + c_read_ready <= 1'b1; + c_busy <= 1'b0; + end + default: begin // STATE_WAIT + ram_cmd <= CMD_NOP; + if(wait_reg == 16'b01) begin + state <= wait_next_state; + if (wait_next_state == STATE_IDLE) c_busy <= 1'b0; + else c_busy <= 1'b1; + end + wait_reg <= wait_reg-1; + end + endcase + + if(|autorefr_cnt) autorefr_cnt <= autorefr_cnt-1; +end + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/soc/serialout.v b/verilog/rtl/ppcpu/soc/serialout.v new file mode 100644 index 0000000..4a3d107 --- /dev/null +++ b/verilog/rtl/ppcpu/soc/serialout.v
@@ -0,0 +1,85 @@ +module serialout ( + input wire clk, + input wire [7:0] data, + output wire sclk, + output reg sdata, + + input wire sdatain, + output wire sdata_pl, + output reg [7:0] btout +); + +reg[22:0] clk_cnt = 0; +always @(posedge clk) begin + clk_cnt <= clk_cnt+1; +end + +reg [3:0] ser_bit = 4'b0; + +wire ref_clk = clk_cnt[20]; +wire ser_clk = clk_cnt[10]; + +reg tx = 0, rt = 0; + +assign sclk = ser_clk & tx; +assign sdata_pl = tx; + +initial btout <= 8'b0; + +always @(negedge ser_clk) begin + case (ser_bit) + 0: begin + sdata <= data[0]; + ser_bit <= 1; + tx <= 1; + btout[0] <= sdatain; + end + 1: begin + sdata <= data[1]; + ser_bit <= 2; + btout[1] <= sdatain; + end + 2: begin + sdata <= data[2]; + ser_bit <= 3; + btout[2] <= sdatain; + end + 3: begin + sdata <= data[3]; + ser_bit <= 4; + btout[3] <= sdatain; + end + 4: begin + sdata <= data[4]; + ser_bit <= 5; + btout[4] <= sdatain; + end + 5: begin + sdata <= data[5]; + ser_bit <= 6; + btout[5] <= sdatain; + end + 6: begin + sdata <= data[6]; + ser_bit <= 7; + btout[6] <= sdatain; + end + 7: begin + sdata <= data[7]; + ser_bit <= 8; + btout[7] <= sdatain; + end + 8: begin + tx <= 0; + if (ref_clk && ~rt) begin + ser_bit <= 0; + rt <= 1; + end else if (~ref_clk) begin + rt <= 0; + end + end + endcase + +end + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/soc/soc.v b/verilog/rtl/ppcpu/soc/soc.v new file mode 100644 index 0000000..4dea827 --- /dev/null +++ b/verilog/rtl/ppcpu/soc/soc.v
@@ -0,0 +1,348 @@ +// Connects ppcpu core with pcpu peripherals for use on original pcpu dev board + + +`include "config.v" + +`define WB_DATA_W 16 +`define WB_SEL_BITS 2 + +module soc ( + input wire i_clk, + input wire i_rst, + + output wire dr_dqml, dr_dqmh, + output wire dr_cs_n, dr_cas_n, dr_ras_n, dr_we_n, dr_cke, + output wire [1:0] dr_ba, + output wire [12:0] dr_a, + inout [15:0] dr_dq, + output wire dr_clk, + + input i_irq, + + output [3:0] pc_leds, + output ser_clk, + output ser_data, + + input uart_rx, + output uart_tx, + + output spi_sck, + output spi_mosi, + input spi_miso, + output [1:0] spi_ss +); + +reg por_n = 1'b0; +reg [15:0] por_cnt = 'b0; +always @(posedge d_clk) begin + por_cnt <= por_cnt + 'b1; + if(&por_cnt) + por_n <= 1'b1; + if(~i_rst) + por_n <= 1'b0; +end + +wire [15:0] dbg_pc, dbg_r0; +//assign pc_leds = {wb_we, wb_ack, sdram_req, sdram_req_active}; +//assign pc_leds = dbg_pc[3:0]; +assign pc_leds = regled; +reg regled; +reg prev_irq; +always @(posedge d_clk) begin + prev_irq <= timer_irq; + regled <= regled ^ (timer_irq ^ prev_irq && timer_irq == 1'b1); +end + +reg [4:0] clk_div; +wire d_clk = clk_div[0]; +wire d_rst = ~(i_rst & por_n); +always @(posedge i_clk) begin + clk_div <= clk_div + 5'b1; +end + +`define MPRJ_IO_PADS 38 +wire [`MPRJ_IO_PADS-1:0] m_io_in; +wire [`MPRJ_IO_PADS-1:0] m_io_out; +wire [`MPRJ_IO_PADS-1:0] m_io_oeb; + +top top ( + .m_io_in(m_io_in), + .m_io_out(m_io_out), + .m_io_oeb(m_io_oeb), + .mgt_wb_clk_i(d_clk), + .mgt_wb_rst_i(d_rst), + .mgt_wb_cyc_i(1'b0), + .mgt_wb_stb_i(1'b0), + .la_oenb({128{1'b1}}), + .mgt_wb_we_i(1'b0), + .mgt_wb_dat_i('b0), + .mgt_wb_sel_i('b0), + .mgt_wb_adr_i('b0), + .la_data_in('b0), + .mgt_wb_ack_o(ignored_wb_ack), + .mgt_wb_dat_o(ignored_wb_dat_o), + .la_data_out(ignored_data_out), + .irq(ignored_irq) +); + +wire [31:0] ignored_wb_dat_o; +wire ignored_wb_ack; +wire [127:0] ignored_data_out; +wire [2:0] ignored_irq; + +// pins to cw bus +wire [`RW-1:0] cw_io_i; +wire [`RW-1:0] cw_io_o; +wire cw_req; +wire cw_dir; +wire cw_ack; +wire cw_err; +wire cw_clk; +wire cw_rst; + +localparam CW_PIN_OFF=8; +assign cw_req = m_io_out[CW_PIN_OFF+0]; +assign cw_dir = m_io_out[CW_PIN_OFF+1]; +assign cw_io_o = m_io_out[CW_PIN_OFF+17:CW_PIN_OFF+2]; +assign m_io_in[CW_PIN_OFF+17:CW_PIN_OFF+2] = cw_io_i; +assign m_io_in[CW_PIN_OFF+18] = cw_ack; +assign m_io_in[CW_PIN_OFF+19] = cw_err; +assign cw_clk = m_io_out[CW_PIN_OFF+20]; +assign cw_rst = m_io_out[CW_PIN_OFF+21]; +assign m_io_in[CW_PIN_OFF+22] = m_irq; +assign m_io_in[CW_PIN_OFF+23] = 1'b0; // split clock + +wire wb_cyc; +wire wb_stb; +wire [`WB_DATA_W-1:0] wb_o_dat; +reg [`WB_DATA_W-1:0] wb_i_dat; +wire [`WB_ADDR_W-1:0] wb_adr; +wire wb_we; +reg wb_ack; +reg wb_err; +wire [`WB_SEL_BITS-1:0] wb_sel; + +wb_decomp wb_decomp ( + .i_clk(cw_clk), + .i_rst(d_rst), + + .cw_io_i(cw_io_o), + .cw_io_o(cw_io_i), + .cw_req(cw_req), + .cw_dir(cw_dir), + .cw_ack(cw_ack), + .cw_err(cw_err), + + .wb_cyc(wb_cyc), + .wb_stb(wb_stb), + .wb_o_dat(wb_o_dat), + .wb_i_dat(wb_i_dat), + .wb_adr(wb_adr), + .wb_we(wb_we), + .wb_ack(wb_ack), + .wb_err(wb_err), + .wb_sel(wb_sel) +); + +/* + * Address map + */ + +localparam UART_BASE = 24'h002000; +localparam UART_END = 24'h002003; + +localparam TIMER_BASE = 24'h002008; +localparam TIMER_END = 24'h00200a; + +localparam IRQC_BASE = 24'h00200c; +localparam IRQC_END = 24'h00200e; + +localparam SPI_BASE = 24'h002010; +localparam SPI_END = 24'h002014; + +localparam SDRAM_BASE = 24'h100000; +localparam SDRAM_END = 24'hffdfff; + +localparam ROM_BASE = 24'hffe000; +localparam ROM_END = 24'hffffff; + + +wire [`RW-1:0] sdram_data_out; +assign sdram_data_out = data_out[15:0]; + +wire [31:0] data_out; + +reg sdram_req; +reg sdram_req_active; + +// Sdram interface to wishbone adapter +always @(posedge cw_clk) begin + if(d_rst) begin + sdram_req <= 1'b0; + sdram_req_active <= 1'b0; + end else if (sdram_req & c_cack & wb_we) begin + sdram_req <= 1'b0; + sdram_req_active <= 1'b0; + end else if (sdram_req & c_cack & c_read_ready & ~wb_we) begin + sdram_req <= 1'b0; + sdram_req_active <= 1'b0; + end else if (sdram_req & c_cack & ~wb_we) begin + sdram_req <= 1'b0; + end else if (sdram_req_active & c_read_ready & ~wb_we) begin + sdram_req_active <= 1'b0; + end else if ((wb_adr >= SDRAM_BASE) & (wb_adr <= SDRAM_END) & wb_cyc & wb_stb & ~sdram_req_active) begin + sdram_req <= 1'b1; + sdram_req_active <= 1'b1; + end +end + +wire sdram_ack = sdram_req_active & ((c_read_ready & ~wb_we) | (c_cack & wb_we)); + +reg prev_stb; +always @(posedge i_clk) begin + prev_stb <= wb_stb; +end + +wire c_busy, c_read_ready, c_cack; +sdram sdram ( + .clk(cw_clk), + .srclk(cw_clk), + .c_addr(wb_adr), + .c_data_in(wb_o_dat), + .c_data_out(data_out), + .c_addr_sel(wb_sel), + .c_read_req(sdram_req & ~wb_we), + .c_write_req(sdram_req & wb_we), + .c_busy(c_busy), + .c_read_ready(c_read_ready), + .c_cack(c_cack), + + .dr_dqml(dr_dqml), .dr_dqmh(dr_dqmh), + .dr_cs_n(dr_cs_n), .dr_cas_n(dr_cas_n), .dr_ras_n(dr_ras_n), .dr_we_n(dr_we_n), .dr_cke(dr_cke), + .dr_ba(dr_ba), + .dr_a(dr_a), + .dr_dq(dr_dq) +); + +assign dr_clk = ~cw_clk;//i_clk; // ram controller depends on setting edges half cycle before ram + +wire [`RW-1:0] rom_data; +soc_rom soc_rom ( + .in_addr(wb_adr), + .out_data(rom_data) +); + +wire [`WB_DATA_W-1:0] uart_wb_i_dat; +wire uart_wb_ack; +uart uart ( + .i_clk(cw_clk), + .i_full_clk(i_clk), + .i_rst(d_rst), + + .tx(uart_tx), + .rx(uart_rx), + + .wb_cyc(wb_cyc), + .wb_stb(wb_stb & ((wb_adr >= UART_BASE) && (wb_adr <= UART_END))), + .wb_adr(wb_adr - UART_BASE), + .wb_we(wb_we), + .wb_i_dat(wb_o_dat), + .wb_o_dat(uart_wb_i_dat), + .wb_ack(uart_wb_ack) +); + +wire timer_irq, timer_wb_ack; +wire [`WB_DATA_W-1:0] timer_wb_i_dat; +timer timer ( + .i_clk(cw_clk), + .i_rst(d_rst), + + .irq(timer_irq), + + .wb_cyc(wb_cyc), + .wb_stb(wb_stb & (wb_adr >= TIMER_BASE && wb_adr <= TIMER_END)), + .wb_adr(wb_adr - TIMER_BASE), + .wb_we(wb_we), + .wb_i_dat(wb_o_dat), + .wb_o_dat(timer_wb_i_dat), + .wb_ack(timer_wb_ack) +); + +wire m_irq, irqc_wb_ack; +wire [`WB_DATA_W-1:0] irqc_wb_i_dat; +irq_ctrl irq_ctrl ( + .i_clk(cw_clk), + .i_rst(d_rst), + + .o_irq(m_irq), + .i_irq({15'b0, timer_irq}), + + .wb_cyc(wb_cyc), + .wb_stb(wb_stb & (wb_adr >= IRQC_BASE && wb_adr <= IRQC_END)), + .wb_adr(wb_adr - IRQC_BASE), + .wb_we(wb_we), + .wb_i_dat(wb_o_dat), + .wb_o_dat(irqc_wb_i_dat), + .wb_ack(irqc_wb_ack) +); + +wire spi_wb_ack; +wire [`WB_DATA_W-1:0] spi_wb_i_dat; +spi spi ( + .i_clk(cw_clk), + .i_rst(d_rst), + + .spi_sck(spi_sck), + .spi_mosi(spi_mosi), + .spi_miso(spi_miso), + .spi_ss(spi_ss), + + .wb_cyc(wb_cyc), + .wb_stb(wb_stb & (wb_adr >= SPI_BASE && wb_adr <= SPI_END)), + .wb_adr(wb_adr - SPI_BASE), + .wb_we(wb_we), + .wb_i_dat(wb_o_dat), + .wb_o_dat(spi_wb_i_dat), + .wb_ack(spi_wb_ack) +); + +always @(*) begin + if ((wb_adr >= UART_BASE) && (wb_adr <= UART_END)) begin + wb_i_dat = uart_wb_i_dat; + wb_ack = wb_cyc & wb_stb; + wb_err = 1'b0; + end else if ((wb_adr >= TIMER_BASE) && (wb_adr <= TIMER_END)) begin + wb_i_dat = timer_wb_i_dat; + wb_ack = timer_wb_ack; + wb_err = 1'b0; + end else if ((wb_adr >= IRQC_BASE) && (wb_adr <= IRQC_END)) begin + wb_i_dat = irqc_wb_i_dat; + wb_ack = irqc_wb_ack; + wb_err = 1'b0; + end else if ((wb_adr >= SPI_BASE) && (wb_adr <= SPI_END)) begin + wb_i_dat = spi_wb_i_dat; + wb_ack = spi_wb_ack; + wb_err = 1'b0; + end else if ((wb_adr >= SDRAM_BASE) && (wb_adr <= SDRAM_END)) begin + wb_i_dat = sdram_data_out; + wb_ack = sdram_ack; + wb_err = 1'b0; + end else if ((wb_adr >= ROM_BASE) && (wb_adr <= ROM_END)) begin + wb_i_dat = rom_data; + wb_ack = wb_cyc & wb_stb; + wb_err = 1'b0; + end else begin + wb_i_dat = 16'b0; + wb_ack = 1'b0; + wb_err = 1'b1; + end +end + +serialout r0_leds ( + .clk(d_clk), + .data(dbg_pc[7:0]), + .sclk(ser_clk), + .sdata(ser_data) +); + +endmodule
diff --git a/verilog/rtl/ppcpu/soc/soc_rom.v b/verilog/rtl/ppcpu/soc/soc_rom.v new file mode 100644 index 0000000..b55521f --- /dev/null +++ b/verilog/rtl/ppcpu/soc/soc_rom.v
@@ -0,0 +1,60 @@ +`include "config.v" + +module soc_rom ( + input [`WB_ADDR_W-1:0] in_addr, + output reg [`RW-1:0] out_data +); + +reg [31:0] instr [127:0]; + +initial begin + $readmemh("/home/piotro/pcpu2/soc/bootloader.hex", instr); +end + +always @* begin + // if (in_addr == `WB_ADDR_W'hffe000) out_data = `RW'h0000; // simple counter + // else if (in_addr == `WB_ADDR_W'hffe001) out_data = `RW'h0000; + // else if (in_addr == `WB_ADDR_W'hffe002) out_data = `RW'h0004; + // else if (in_addr == `WB_ADDR_W'hffe003) out_data = `RW'h0001; + // else if (in_addr == `WB_ADDR_W'hffe004) out_data = `RW'h0008; + // else if (in_addr == `WB_ADDR_W'hffe005) out_data = `RW'h0001; + // else if (in_addr == `WB_ADDR_W'hffe006) out_data = `RW'h000e; + // else if (in_addr == `WB_ADDR_W'hffe007) out_data = `RW'h0002; + // if (in_addr == `WB_ADDR_W'hffe000) out_data = `RW'h0000; // counter with memory access + // else if (in_addr == `WB_ADDR_W'hffe001) out_data = `RW'h0000; + // else if (in_addr == `WB_ADDR_W'hffe002) out_data = `RW'h0004; + // else if (in_addr == `WB_ADDR_W'hffe003) out_data = `RW'h0000; + // else if (in_addr == `WB_ADDR_W'hffe004) out_data = `RW'h0005; + // else if (in_addr == `WB_ADDR_W'hffe005) out_data = `RW'h0000; + // else if (in_addr == `WB_ADDR_W'hffe006) out_data = `RW'h0004; + // else if (in_addr == `WB_ADDR_W'hffe007) out_data = `RW'h0000; + // else if (in_addr == `WB_ADDR_W'hffe008) out_data = `RW'h0002; + // else if (in_addr == `WB_ADDR_W'hffe009) out_data = `RW'h0000; + // else if (in_addr == `WB_ADDR_W'hffe00a) out_data = `RW'h0008; + // else if (in_addr == `WB_ADDR_W'hffe00b) out_data = `RW'h0002; + // else if (in_addr == `WB_ADDR_W'hffe00c) out_data = `RW'h000e; + // else if (in_addr == `WB_ADDR_W'hffe00d) out_data = `RW'h0002; + // if (in_addr == `WB_ADDR_W'hffe000) out_data = `RW'h0000; // uart test + // else if (in_addr == `WB_ADDR_W'hffe001) out_data = `RW'h0000; + // else if (in_addr == `WB_ADDR_W'hffe002) out_data = `RW'h0084; + // else if (in_addr == `WB_ADDR_W'hffe003) out_data = `RW'h0000; + // else if (in_addr == `WB_ADDR_W'hffe004) out_data = `RW'h0002; + // else if (in_addr == `WB_ADDR_W'hffe005) out_data = `RW'h0000; + // else if (in_addr == `WB_ADDR_W'hffe006) out_data = `RW'h001b; + // else if (in_addr == `WB_ADDR_W'hffe007) out_data = `RW'h0002; + // else if (in_addr == `WB_ADDR_W'hffe008) out_data = `RW'h010e; + // else if (in_addr == `WB_ADDR_W'hffe009) out_data = `RW'h0002; + // else if (in_addr == `WB_ADDR_W'hffe00a) out_data = `RW'h0405; + // else if (in_addr == `WB_ADDR_W'hffe00b) out_data = `RW'h0004; + // else if (in_addr == `WB_ADDR_W'hffe00c) out_data = `RW'h0488; + // else if (in_addr == `WB_ADDR_W'hffe00d) out_data = `RW'h0001; + // else if (in_addr == `WB_ADDR_W'hffe00e) out_data = `RW'h000e; + // else if (in_addr == `WB_ADDR_W'hffe00f) out_data = `RW'h0002; + // else out_data = `RW'b0; + if (in_addr[0]) + out_data = instr[in_addr[11:1]][31:16]; + else + out_data = instr[in_addr[11:1]][15:0]; +end + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/soc/spi.v b/verilog/rtl/ppcpu/soc/spi.v new file mode 100644 index 0000000..a3adc37 --- /dev/null +++ b/verilog/rtl/ppcpu/soc/spi.v
@@ -0,0 +1,144 @@ +module spi ( + input i_clk, + input i_rst, + + output spi_sck, + output reg spi_mosi, + input spi_miso, + output reg [1:0] spi_ss, + + input [23:0] wb_adr, + input wb_cyc, + input wb_stb, + output wb_ack, + input wb_we, + input [15:0] wb_i_dat, + output reg [15:0] wb_o_dat +); + +// MSB first +wire CPOL = mode_reg[5]; +wire CPHA = mode_reg[4]; + +reg spi_sck_s; +assign spi_sck = spi_sck_s ^ (~CPOL); + +wire spi_start_req; +reg req_in_progress; +reg req_end_xor; +reg req_step; +reg [2:0] req_bit; +reg [7:0] tx_byte, rx_byte; + +always @(posedge spi_logic_clk) begin + if (i_rst) begin + spi_sck_s <= 1'b1; + spi_mosi <= 1'b1; + + req_step <= 1'b0; + req_end_xor <= 1'b0; + end else if (req_in_progress & ~req_step) begin + spi_sck_s <= CPHA; + rx_byte[req_bit] <= spi_miso; + + req_bit <= req_bit - 3'b1; + req_step <= 1'b1; + req_in_progress <= (req_bit != 3'b0); + req_end_xor <= req_end_xor^(req_bit == 3'b0); + end else if (req_in_progress & req_step) begin + spi_sck_s <= ~CPHA; + spi_mosi <= tx_byte[req_bit]; + + req_step <= 1'b0; + end else if (spi_start_req) begin + spi_sck_s <= ~CPHA; + spi_mosi <= tx_byte[7]; + + req_step <= 1'b0; + req_bit <= 3'd7; + req_in_progress <= 1'b1; + end else begin + spi_sck_s <= 1'b1; + spi_mosi <= 1'b1; + end +end + +reg xfer_start_flag; +reg spi_busy; +always @(posedge i_clk) begin + if (i_rst) begin + xfer_start_flag <= 1'b0; + spi_busy <= 1'b0; + mode_reg <= 6'b000110; + spi_ss <= 2'b11; + end else begin + if (spi_req_end) + spi_busy <= 1'b0; + + if (wb_cyc & wb_stb & wb_we & (wb_adr == 24'b0)) begin + xfer_start_flag <= ~xfer_start_flag; + tx_byte <= wb_i_dat[7:0]; + spi_busy <= 1'b1; + end else if (wb_cyc & wb_stb & wb_we & (wb_adr == 24'h3)) begin + mode_reg <= wb_i_dat[5:0]; + end else if (wb_cyc & wb_stb & wb_we & (wb_adr == 24'h4)) begin + spi_ss <= wb_i_dat[1:0]; + end + end +end + +always @(*) begin + if (wb_adr == 24'h1) + wb_o_dat = {8'b0, rx_byte}; + else if (wb_adr == 24'h2) + wb_o_dat = {15'b0, spi_busy}; + else if (wb_adr == 24'h3) + wb_o_dat = {10'b0, mode_reg}; + else if (wb_adr == 24'h4) + wb_o_dat = {15'b0, spi_ss}; + else + wb_o_dat = 16'b0; +end + + +reg [5:0] mode_reg; + +wire [3:0] clk_div = mode_reg[3:0]; +reg [9:0] clk_cnt; +assign spi_logic_clk = clk_cnt[clk_div]; +always @(posedge i_clk) begin + if (i_rst) + clk_cnt <= 1'b0; + else + clk_cnt <= clk_cnt + 1'b1; +end + +assign wb_ack = wb_cyc & wb_stb; + +// CDC + +reg [2:0] d_xfer_xor_sync; +always @(posedge spi_logic_clk) begin + if(i_rst) begin + d_xfer_xor_sync[2:0] <= 3'b0; + end else begin + d_xfer_xor_sync[0] <= xfer_start_flag; + d_xfer_xor_sync[1] <= d_xfer_xor_sync[0]; + d_xfer_xor_sync[2] <= d_xfer_xor_sync[1]; + end +end +assign spi_start_req = (d_xfer_xor_sync[1] ^ d_xfer_xor_sync[2]) & ~i_rst; + +reg [2:0] d_req_end_sync; +always @(posedge i_clk) begin + if(i_rst) begin + d_req_end_sync[2:0] <= 3'b0; + end else begin + d_req_end_sync[0] <= req_end_xor; + d_req_end_sync[1] <= d_req_end_sync[0]; + d_req_end_sync[2] <= d_req_end_sync[1]; + end +end +wire spi_req_end = (d_req_end_sync[1] ^ d_req_end_sync[2]) & ~i_rst; + +endmodule \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/soc/timer.v b/verilog/rtl/ppcpu/soc/timer.v new file mode 100644 index 0000000..5da2b68 --- /dev/null +++ b/verilog/rtl/ppcpu/soc/timer.v
@@ -0,0 +1,59 @@ +module timer ( + input i_clk, + input i_rst, + output reg irq, + + input [23:0] wb_adr, + input wb_cyc, + input wb_stb, + output wb_ack, + input wb_we, + input [15:0] wb_i_dat, + output [15:0] wb_o_dat +); + +reg [15:0] timer_cnt; + +reg [15:0] pre_div_cnt; +reg [3:0] clk_div; + +reg [15:0] reset_val; + +always @(posedge i_clk) begin + if(i_rst) begin + timer_cnt <= 16'b0; + pre_div_cnt <= 16'b0; + end else if(wb_cyc & wb_stb & wb_we & (wb_adr == 2'b0)) begin + timer_cnt <= wb_i_dat; + end else begin + pre_div_cnt <= pre_div_cnt + 16'b1; + + if(pre_div_cnt >= (1<<clk_div) - 16'b1) begin + timer_cnt <= timer_cnt + 16'b1; + pre_div_cnt <= 16'b0; + + if(timer_cnt == 16'hffff) + timer_cnt <= reset_val; + end + end +end + +always @(posedge i_clk) begin + irq <= (timer_cnt == 16'hffff); +end + +always @(posedge i_clk) begin + if(i_rst) begin + reset_val <= 16'b0; + clk_div <= 4'b0; + end else if(wb_cyc & wb_stb & wb_we & (wb_adr == 2'b1)) begin + clk_div <= wb_i_dat[3:0]; + end else if(wb_cyc & wb_stb & wb_we & (wb_adr == 2'b10)) begin + reset_val <= wb_i_dat; + end +end + +assign wb_o_dat = timer_cnt; +assign wb_ack = (wb_cyc & wb_stb); + +endmodule
diff --git a/verilog/rtl/ppcpu/soc/uart.v b/verilog/rtl/ppcpu/soc/uart.v new file mode 100644 index 0000000..9cc2762 --- /dev/null +++ b/verilog/rtl/ppcpu/soc/uart.v
@@ -0,0 +1,214 @@ +module uart ( + input i_clk, + input i_full_clk, + input i_rst, + + input rx, + output reg tx, + + input wb_cyc, wb_stb, wb_we, + output wb_ack, + input [23:0] wb_adr, + input [15:0] wb_i_dat, + output reg [15:0] wb_o_dat +); + +localparam BAUD_RATE = 115200; +localparam OVERSAMPLE = 8; +localparam OVERSAMPLE_LOG = 3; +localparam CLOCK_FREQ = 50_000_000; +localparam UART_CLOCK_DIV = CLOCK_FREQ/(BAUD_RATE*2); +localparam OSMPL_CLOCK_DIV = CLOCK_FREQ/(BAUD_RATE*OVERSAMPLE*2); +localparam RX_BUFF_SIZE = 8; +localparam TX_BUFF_SIZE = 8; + +reg uart_os_clk = 1'b0; +reg [5:0] uart_os_clk_cnt = 6'b0; +reg uart_clk = 1'b0; +reg [9:0] uart_clk_cnt = 10'b0; +always @(posedge i_full_clk) begin + uart_os_clk_cnt <= uart_os_clk_cnt + 6'b1; + uart_clk_cnt <= uart_clk_cnt + 6'b1; + + if (uart_os_clk_cnt == 27) begin + uart_os_clk_cnt <= 6'b0; + uart_os_clk <= ~uart_os_clk; + end + + if (uart_clk_cnt == UART_CLOCK_DIV) begin + uart_clk_cnt <= 10'b0; + uart_clk <= ~uart_clk; + end +end + +// RECEIVE +// reg rx_active = 1'b0, rx_stop = 1'b0; +// reg [OVERSAMPLE_LOG:0] rx_os_cnt; +reg [7:0] rx_result; +reg [2:0] rx_res_bit; + +//wire rx_submit = (rx_stop & (rx_os_cnt == 0)); +wire rx_submit = (sub_clk_cnt == 4'b0111) && (state == 4'b1001); +//assign dbg_trig = rx_submit & ~rx; + +// I DONT KNOW WHY but this code from old cpu works just fine, +// but preetier new code that seems to behave in the same way misses the stop bits. WHY??? +reg [3:0] state = 4'b0; +reg [3:0] sub_clk_cnt = 4'b0; +//reg r1_trig = 1'b0; +always @(posedge uart_os_clk) begin + case (state) + 4'b0: begin + // if start bit + if(rx == 1'b0) begin + state <= 1'b1; + end + end + 4'b1001: begin + if(sub_clk_cnt == 4'b0111) begin + sub_clk_cnt <= 4'b0; + state <= 4'b0; + //r1_trig <= ~r1_trig; + end else begin + sub_clk_cnt <= sub_clk_cnt+4'b1; + end + end + default: begin // default read bit + if((state != 4'b1 && sub_clk_cnt == 4'b0111) || sub_clk_cnt == 4'b1010) begin //delay first clock by one and half + rx_result[state-4'b1] <= rx; + state <= state+4'b1; + sub_clk_cnt <= 4'b0; + //r1_trig <= ~r1_trig; + end else begin + sub_clk_cnt <= sub_clk_cnt+4'b1; + end + end + endcase +end + +// reg r2_trig = 1'b0; +// always @(posedge uart_os_clk) begin +// if (i_rst) begin +// rx_active <= 1'b0; +// rx_os_cnt <= 'b0; +// rx_stop <= 1'b0; +// end else begin +// if (~rx_active & ~rx) begin +// rx_active <= 1'b1; +// rx_res_bit <= 'b0; +// rx_os_cnt <= 4'b1010; +// end else if (rx_stop & (rx_os_cnt == 0)) begin +// rx_active <= 1'b0; +// rx_stop <= 1'b0; +// r2_trig <= ~r2_trig; +// //dbg_trig <= ~dbg_trig; +// end else if (rx_active & ~rx_stop & (rx_os_cnt == 0)) begin +// rx_os_cnt <= 4'b0111; +// r2_trig <= ~r2_trig; +// //rx_result[rx_res_bit] <= rx; +// rx_res_bit <= rx_res_bit + 1'b1; +// rx_stop <= (rx_res_bit == 3'b111); +// //dbg_trig <= ~dbg_trig; +// end else if (rx_active) begin +// rx_os_cnt <= rx_os_cnt - 1'b1; +// end +// end +// end + + +reg [7:0] rx_fifo [RX_BUFF_SIZE-1:0]; +reg [2:0] rx_write_ptr, rx_read_ptr; + +always @(posedge uart_os_clk) begin + if (i_rst) begin + rx_write_ptr <= 3'b0; + end else if (rx_submit) begin + rx_fifo[rx_write_ptr] <= rx_result; + rx_write_ptr <= rx_write_ptr + 3'b1; + end +end + +wire rx_data_available = |(rx_read_ptr ^ rx_write_ptr); +reg [2:0] rx_prev_data; +wire rx_irq = (rx_prev_data == 3'b0 & ((rx_write_ptr-rx_read_ptr) != 3'b0)); + +always @(posedge i_clk) begin + if (i_rst) begin + rx_read_ptr <= 3'b0; + end else begin + rx_prev_data <= rx_write_ptr-rx_read_ptr; + if (wb_cyc & wb_stb & ~wb_we & wb_adr == 24'h1 & rx_data_available) begin + rx_read_ptr <= rx_read_ptr + 3'b1; + rx_prev_data <= (rx_write_ptr-rx_read_ptr-3'b1); + end + end +end + +// TRANSMIT + +reg [1:0] tx_state; +wire tx_ready = (tx_state == 2'b0); +wire tx_start = tx_data_avail; +reg [7:0] tx_data; +reg [2:0] tx_data_cnt; + +always @(posedge uart_clk) begin + if (i_rst) begin + tx <= 1'b1; + tx_state <= 2'b0; + end else if ((tx_state == 2'b0) & tx_start) begin + tx <= 1'b0; // start bit + tx_data_cnt <= 3'b0; + tx_state <= 2'b1; + end else if (tx_state == 2'b1) begin + tx <= tx_data[tx_data_cnt]; + tx_data_cnt <= tx_data_cnt + 1'b1; + if (&tx_data_cnt) + tx_state <= 2'b10; + end else if (tx_state == 2'b10) begin + tx <= 1'b1; // stop bit + tx_state <= 2'b0; + end +end + +reg [7:0] tx_fifo [RX_BUFF_SIZE-1:0]; +reg [2:0] tx_write_ptr, tx_read_ptr; +wire tx_data_avail = |(tx_write_ptr^tx_read_ptr); +wire tx_full = (tx_write_ptr+3'b1) == tx_read_ptr; + +always @(posedge uart_clk) begin + if (i_rst) begin + tx_read_ptr <= 3'b0; + end else if (tx_ready & tx_data_avail) begin + tx_read_ptr <= tx_read_ptr + 3'b1; + tx_data <= tx_fifo[tx_read_ptr]; + end +end + +reg [2:0] tx_prev_data; +wire tx_empty_irq = (tx_prev_data != 3'b0) && ((tx_write_ptr-tx_read_ptr) == 3'b0); + +always @(posedge i_clk) begin + if (i_rst) begin + tx_write_ptr <= 3'b0; + end else begin + tx_prev_data <= (tx_write_ptr-tx_read_ptr); + if (wb_cyc & wb_stb & wb_we & wb_adr == 24'h2) begin + tx_write_ptr <= tx_write_ptr + 3'b1; + tx_fifo[tx_write_ptr] <= wb_i_dat[7:0]; + end + end +end + +assign wb_ack = wb_cyc & wb_stb; +always @* begin + if (wb_adr == 24'h0) begin + wb_o_dat = {14'b0, ~tx_full, rx_data_available}; + end else if (wb_adr == 24'h1) begin + wb_o_dat = rx_fifo[rx_read_ptr]; + end else begin + wb_o_dat = 16'b0; + end +end + +endmodule
diff --git a/verilog/rtl/ppcpu/test/arch/Makefile b/verilog/rtl/ppcpu/test/arch/Makefile new file mode 100644 index 0000000..4a3fe4d --- /dev/null +++ b/verilog/rtl/ppcpu/test/arch/Makefile
@@ -0,0 +1,25 @@ +ALL_TESTS=\ + fncall,\ + sreg0 + +ALL_TESTS_TRG=$(patsubst %, %.hex, $(ALL_TESTS)) + +.PHONY: test clean + +test: $(ALL_TESTS_TRG) $(ALL_TESTS) + +%: %.hex arch + ./arch < $< + @echo ">> test $@ passed" + +%.hex: %.s + python3 ~/opt/pcpu-tools/pas.py $< -o $@ + +arch: arch.cpp + verilator --cc -I../../rtl/ --exe --trace core.v arch.cpp \ + && make -C obj_dir/ -f Vcore.mk Vcore \ + && cp obj_dir/Vcore arch + +clean: + rm -f arch *.hex *.vcd + rm -rf obj_dir/
diff --git a/verilog/rtl/ppcpu/test/arch/aarith.s b/verilog/rtl/ppcpu/test/arch/aarith.s new file mode 100644 index 0000000..79a54d9 --- /dev/null +++ b/verilog/rtl/ppcpu/test/arch/aarith.s
@@ -0,0 +1,33 @@ +start: + ldi r0, 0x008a + sex r1, r0 + cmi r1, 0xff8a + jne err + + ldi r0, 0b1011 + sri r0, r0, 2 + sli r0, r0, 1 + cmi r0, 0b100 + jne err + + ldi r0, 0x8003 + sai r0, r0, 1 + cmi r0, 0xc001 + jne err + ldi r0, 0x8003 + sai r0, r0, 2 + cmi r0, 0xe000 + jne err + ldi r0, 0x4003 + ldi r1, 1 + sar r0, r0, r1 + cmi r0, 0x2001 + jne err + + jmp pass +err: + jmp err + +.org 0x1000 +pass: + jmp pass \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/test/arch/arch.cpp b/verilog/rtl/ppcpu/test/arch/arch.cpp new file mode 100644 index 0000000..8dac934 --- /dev/null +++ b/verilog/rtl/ppcpu/test/arch/arch.cpp
@@ -0,0 +1,129 @@ +#include "Vcore.h" +#include "Vcore___024root.h" + +#include <iostream> +#include <verilated_vcd_c.h> +#include <verilated.h> + +#define MAX_TEST_TIME 200 +uint64_t sim_time = 0; + +std::map<unsigned int, unsigned int> instr_mem; +std::map<unsigned int, unsigned short> data_mem; + +int req_time = 0; + +void tick(Vcore *dut, VerilatedVcdC* vcd) { + do { + dut->i_clk ^= 1; + dut->eval(); + vcd->dump(sim_time++); + } while(dut->i_clk); + + std::cout<<"."; +} + +void cpu_reset(Vcore* dut, VerilatedVcdC* v_vcd) { + dut->i_rst = 1; + for(int i=0; i<4; i++) + tick(dut, v_vcd); + dut->i_rst = 0; + req_time = 0; +} + +void sim_mem(Vcore* d) { + d->i_req_data_valid = 0; + if(d->o_req_active && !d->i_req_data_valid) { + static int req_addr = -1; + if(!req_time) + req_addr = d->o_req_addr; + + if(++req_time >= 2) { + req_time = 0; + d->i_req_data_valid = 1; + d->i_req_data = instr_mem[req_addr]; + std::cout<<"REQF "<<std::hex<<req_addr<<" r:"<<d->i_req_data<<"\n"; + } + } +} + +int data_mem_req_time = 0; +int data_mem_req_addr = 0, data_mem_req_data = 0; +bool data_mem_req_we = false; +void sim_data_mem(Vcore* d) { + d->i_mem_ack = 0; + if(d->o_mem_req && !d->i_mem_ack) { + if (!req_time) { + data_mem_req_addr = d->o_mem_addr; + data_mem_req_data = d->o_mem_data; + data_mem_req_we = d->o_mem_we; + } + + if(++data_mem_req_time >= 2) { + data_mem_req_time = 0; + d->i_mem_ack = 1; + assert(data_mem_req_addr < 100); + if(data_mem_req_we) + data_mem[data_mem_req_addr] = d->o_mem_data; + else + d->i_mem_data = data_mem[data_mem_req_addr]; + std::cout<<"memreq a"<<data_mem_req_addr<<'d'<<(data_mem_req_we ? data_mem_req_data : d->i_mem_data)<< + 'w'<<data_mem_req_we<<'\n'; + } + } +} + +void load_hex() { + std::string line; + while(std::cin>>line) { + if(line == ":00000001FF") + break; + + if(line.size() != 19 || line[0] != ':') { + std::cerr<<"invalid input"; + exit(1); + } + + std::string addr = line.substr(3, 4); + std::string data = line.substr(9, 8); + + instr_mem[std::stoul(addr, nullptr, 16)] = std::stoul(data, nullptr, 16); + } +} + +int main() { + Vcore *dut = new Vcore; + + Verilated::traceEverOn(true); + VerilatedVcdC *vcd = new VerilatedVcdC; + dut->trace(vcd, 99); + vcd->open("waveform.vcd"); + + srand(8); + load_hex(); + + cpu_reset(dut, vcd); + + bool fail = true; + while (sim_time <= MAX_TEST_TIME) { + sim_mem(dut); + sim_data_mem(dut); + + tick(dut, vcd); + + std::cout<<"r0:"<<dut->dbg_r0<<" pc:"<<dut->dbg_pc + <<(dut->rootp->core__DOT__execute__DOT__exec_submit ? '<' : ' ')<<'\n'; + + if(dut->dbg_pc == 0x1000) { + fail = false; + break; + } + } + + dut->final(); + vcd->close(); + + if(fail) + std::cerr<<"test failed\n"; + return fail; +}
diff --git a/verilog/rtl/ppcpu/test/arch/fncall.s b/verilog/rtl/ppcpu/test/arch/fncall.s new file mode 100644 index 0000000..769d14a --- /dev/null +++ b/verilog/rtl/ppcpu/test/arch/fncall.s
@@ -0,0 +1,17 @@ +start: + ldi r0, 1 + jal r6, inc + add r0, r0, r0 + jal r6, inc + cmi r0, 5 + jeq pass +fail: + jmp fail + +inc: + adi r0, r0, 1 + srs r6, 0 + +.org 0x1000 +pass: + jmp pass \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/test/arch/sreg0.s b/verilog/rtl/ppcpu/test/arch/sreg0.s new file mode 100644 index 0000000..031b6da --- /dev/null +++ b/verilog/rtl/ppcpu/test/arch/sreg0.s
@@ -0,0 +1,29 @@ +ldi r0, 2 +srl r1, 0 +cmi r1, 1 +jne err +ldi r1, 8 +srs r1, 0 + +err: +ldi r0, 4 +jmp err + +;pc = 8 +ldi r0, 5 +jal r6, next +nop + +next: +cmi r6, 10 +jne err +srl r0, 0 +adi r0, r0, 4 +srs r0, 0 + +jmp err +jmp pass +jmp err + +.org 0x1000 +pass: \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/test/tb_core.cpp b/verilog/rtl/ppcpu/test/tb_core.cpp new file mode 100644 index 0000000..dc35763 --- /dev/null +++ b/verilog/rtl/ppcpu/test/tb_core.cpp
@@ -0,0 +1,344 @@ +#include "Vcore.h" +#include "Vcore___024root.h" + +#include <iostream> +#include <verilated_vcd_c.h> +#include <verilated.h> + +#define MAX_TEST_TIME 200 +uint64_t sim_time = 0; +uint64_t test_time = 0; + +int req_time = 0; +int i_seq = 1; + +void tick(Vcore *dut, VerilatedVcdC* vcd) { + do { + dut->i_clk ^= 1; + dut->eval(); + vcd->dump(sim_time++); + } while(dut->i_clk); + + std::cout<<"."; +} + +void cpu_reset(Vcore* dut, VerilatedVcdC* v_vcd) { + dut->i_rst = 1; + for(int i=0; i<4; i++) + tick(dut, v_vcd); + dut->i_rst = 0; + req_time = 0; +} + +void sim_mem(Vcore* d, std::vector<int>& instr, bool randt=false) { + d->i_req_data_valid = 0; + if(d->o_req_active && !d->i_req_data_valid) { + static int req_addr = -1; + if(!req_time) + req_addr = d->o_req_addr; + + if(++req_time >= (randt ? (rand()%3)+1 : 2)) { + req_time = 0; + d->i_req_data_valid = 1; + if (req_addr >= instr.size()) { + d->i_req_data = 0; // NOP + } else { + d->i_req_data = instr[req_addr]; + } + std::cout<<"REQF "<<std::hex<<req_addr<<" r:"<<d->i_req_data<<"\n"; + } + } +} + +int data_mem_req_time = 0; +int data_mem_req_addr = 0, data_mem_req_data = 0; +bool data_mem_req_we = false; +short data_mem[100]; +void sim_data_mem(Vcore* d) { + d->i_mem_ack = 0; + if(d->o_mem_req && !d->i_mem_ack) { + if (!req_time) { + data_mem_req_addr = d->o_mem_addr; + data_mem_req_data = d->o_mem_data; + data_mem_req_we = d->o_mem_we; + } + + if(++data_mem_req_time >= 2) { + data_mem_req_time = 0; + d->i_mem_ack = 1; + assert(data_mem_req_addr < 100); + if(data_mem_req_we) + data_mem[data_mem_req_addr] = d->o_mem_data; + else + d->i_mem_data = data_mem[data_mem_req_addr]; + std::cout<<"memreq a"<<data_mem_req_addr<<'d'<<(data_mem_req_we ? data_mem_req_data : d->i_mem_data)<< + 'w'<<data_mem_req_we<<'\n'; + } + } +} + +bool test_simple (Vcore* dut, VerilatedVcdC* v_vcd) { + cpu_reset(dut, v_vcd); + /* listing + * ldi r0, 1 + * ldi r1, 2 + * add r0, r0, r1 + * add r0, r0, r0 + * adi r0, r0, 1 + * sub r0, r0, r1 + */ + std::vector <int> instr = {0, 0x00010004, 0x00020084, 0x00002007, 0x00000007, 0x00010008, 0x0000200A, 0x0, 0x0}; + test_time = sim_time; + while (sim_time-test_time < MAX_TEST_TIME) { + // simulate external devices + sim_mem(dut, instr, true); + + // clock tick + tick(dut, v_vcd); + + std::cout<<"r0:"<<dut->dbg_r0<<" pc:"<<dut->dbg_pc<<'\n'; + + if(dut->dbg_pc >= instr.size()) + break; + } + + if(dut->dbg_r0 != 5) { + std::cout<<"test_simple failed\n"; + return false; + } + std::cout<<"test_simple passed\n"; + return true; +} + +bool test_mispredict (Vcore* dut, VerilatedVcdC* v_vcd) { + cpu_reset(dut, v_vcd); + /* listing + ldi r0, 1 + jmp b + mp: + ldi r0, 4 + jmp mp + b: + cmi r0, 1 + jne mp ; MISPREDICTION (pred taken) + c: + ldi r0, 8 + jmp c + ldi r0, 5 + nop + */ + + std::vector <int> instr = { + 0x0000010004, + 0x000004000E, + 0x0000040004, + 0x000002000E, + 0x000001000D, + 0x000002038E, + 0x0000080004, + 0x000006000E, + 0x0000050004, + 0x0000000000}; + + test_time = sim_time; + int end_it = 0; + while (sim_time-test_time < MAX_TEST_TIME) { + // simulate external devices + sim_mem(dut, instr); + + // clock tick + tick(dut, v_vcd); + + std::cout<<"r0:"<<dut->dbg_r0<<" pc:"<<dut->dbg_pc<<'\n'; + + if(dut->dbg_pc == 0x7) + end_it++; + if(dut->dbg_pc >= instr.size() || end_it >= 3) + break; + } + + if(dut->dbg_r0 != 8) { + std::cout<<"test_mispredict failed\n"; + return false; + } + std::cout<<"test_mispredict passed\n"; + return true; +} + +bool test_mem_access(Vcore* dut, VerilatedVcdC* vcd) { + cpu_reset(dut, vcd); + /* listing + ldi r0, 3 + std r0, 2 + ldd r1, 2 + ldi r3, 10 + sto r3, r1, 1 + ldi r2, 1 + ldo r0, r2, 1 + ldo r1, r2, 3 + add r0, r0, r1 + */ + std::vector <int> instr = { + 0x00030004, + 0x00020005, + 0x00020082, + 0x000A0184, + 0x00012C06, + 0x00010104, + 0x00010803, + 0x00030883, + 0x00002007, + 0x0, 0x0, 0x0}; + + test_time = sim_time; + while (sim_time-test_time < MAX_TEST_TIME) { + sim_mem(dut, instr); + sim_data_mem(dut); + + tick(dut, vcd); + + std::cout<<"r0:"<<dut->dbg_r0<<" pc:"<<dut->dbg_pc<<'\n'; + + if(dut->dbg_pc >= instr.size()) + break; + } + + if(dut->dbg_r0 != 13) { + std::cout<<"test_mem_access failed\n"; + return false; + } + std::cout<<"test_mem_access passed\n"; + return true; +} + +bool test_interupts(Vcore* dut, VerilatedVcdC* vcd) { + cpu_reset(dut, vcd); + srand(3); + /* listing + jmp start + jmp irq + + start: + ldi r1, 0 + ldi r0, 0b101 + srs r0, 1 + ldi r0, 0 + ldi r2, 0x10 + ; overcompicated counter to test functions under interrupts + loop: + adi r0, r0, 1 + + std r0, 2 + ldi r0, 900 + ldd r0, 2 + + cmi r0, 0 + jge fwd_jmp + ldi r0, 800 + fwd_jmp: + + cmp r0, r2 + jlt loop + + jmp pass_ver + + irq: + srl r3, 4 + adi r1, r1, 1 + srs r3, 4 ; recover arithemtic flags + nop ; nop instruction will clear interrrupt in custom tb + irt + + pass_ver: ; with r0 and r1 verification in tb + jmp pass_ver + */ + std::vector <int> instr = { + 0x0002000E, + 0x0011000E, + 0x00000084, + 0x00050004, + 0x00010011, + 0x00000004, + 0x00100104, + 0x00010008, + 0x00020005, + 0x03840004, + 0x00020002, + 0x0000000D, + 0x000E030E, + 0x03200004, + 0x0000400C, + 0x0007018E, + 0x0016000E, + 0x00040190, + 0x00010488, + 0x00040C11, + 0x00000000, + 0x0000001E, + 0x0016000E}; + + test_time = sim_time; + int irqc = 0; + while (sim_time-test_time < MAX_TEST_TIME*100) { + sim_mem(dut, instr); + sim_data_mem(dut); + + if(rand() % 100 < 5 && !dut->i_irq && dut->dbg_pc != 0x14) { + std::cout<<"irq\n"; + irqc++; + dut->i_irq = 1; + } + + tick(dut, vcd); + + if(dut->dbg_pc == 0x14) { + std::cout<<"cli\n"; + dut->i_irq = 0; // cli + } + + if(dut->dbg_pc >= instr.size()-1) { + std::cout<<"end"; + break; + } + } + + int r0 = dut->dbg_r0; + int r1 = dut->rootp->core__DOT__execute__DOT__rf__DOT____Vcellout__rf_regs__BRA__1__KET____DOT__rf_reg__o_d; + std::cout<<"r0: "<<r0<<" r1: "<<r1<<" ex:"<<irqc<<'\n'; + if(r0 != 0x10 || r1 != irqc) { + std::cout<<"test_interrupt failed\n"; + return false; + } + std::cout<<"test_interrupt passed\n"; + return true; +} + +int main() { + Vcore *dut = new Vcore; + + Verilated::traceEverOn(true); + VerilatedVcdC *v_vcd = new VerilatedVcdC; + dut->trace(v_vcd, 99); + v_vcd->open("waveform.vcd"); + + bool fail = false; + srand(2); + + fail |= !test_simple(dut, v_vcd); + fail |= !test_mispredict(dut, v_vcd); + fail |= !test_mem_access(dut, v_vcd); + fail |= !test_interupts(dut, v_vcd); + // Tests in assembly format are located in test/arch/, and are runned in basic core testbench + + dut->final(); + v_vcd->close(); + delete dut; + + std::cout<<'\n'; + if(fail) + std::cout<<"SOME TESTS FAILED\n"; + else + std::cout<<"ALL TESTS PASSED\n"; + std::cout<<'\n'; + return fail; +}
diff --git a/verilog/rtl/ppcpu/test/tb_decode.cpp b/verilog/rtl/ppcpu/test/tb_decode.cpp new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/verilog/rtl/ppcpu/test/tb_decode.cpp
diff --git a/verilog/rtl/ppcpu/test/tb_fetch.cpp b/verilog/rtl/ppcpu/test/tb_fetch.cpp new file mode 100644 index 0000000..723e274 --- /dev/null +++ b/verilog/rtl/ppcpu/test/tb_fetch.cpp
@@ -0,0 +1,169 @@ +#include "Vfetch.h" +#include "Vfetch___024root.h" + +#include <iostream> +#include <verilated_vcd_c.h> +#include <verilated.h> + +#define MAX_SIM_TIME 200 +uint64_t sim_time = 0; + +int req_time = 0; +int req_resp_time = 0; +int i_seq = 1; + +std::vector <int> res_ins; +int instructions[] = {0, 0xffff, 0x5000e, 0x0000e, 0xfffd, 0x8010e /*pred not taken*/, 0xfffa, 0x7000e, 0xfffb}; +std::vector <int> exp_ins = {0, 0xffff, 0x5000e, 0x8010e, 0xfffa, 0x7000e, 0x7000e}; +int exp_by_idx[9] = {0xffff, 0x5000e, 0x8010e, 0x0, 0x8010e, 0xfffa, 0x7000e, 0xfffb, -1}; +int next_exp_insn = -2; + +void sim_mem(Vfetch* d) { + if(d->i_clk) + return; + + d->i_req_data_valid = 0; + if(d->o_req_active && !d->i_req_data_valid) { + static int req_addr = -1; + if(!req_time) { + std::cout<<"NREQ "<<d->o_req_addr<<'\n'; + req_addr = d->o_req_addr; + req_resp_time = (rand()%3) + 1; + } + + if(++req_time >= req_resp_time) { + req_time = 0; + d->i_req_data_valid = 1; + assert(req_addr < 8); + d->i_req_data = instructions[req_addr]; + std::cout<<"REQF "<<req_addr<<" r:"<<d->i_req_data<<"\n"; + } + } +} + +bool first_flush = true; +int flush_rand_delay_by = 0; +void sim_cpu(Vfetch* d, bool flush_test=false) { + if(d->i_clk) + return; + + if(d->o_submit) { + assert(d->i_next_ready); + std::cout<<"PIPE "<<std::hex<<d->o_instr<<"(fetch_pc: "<<d->rootp->fetch__DOT__fetch_pc<<")\n"; + res_ins.push_back(d->o_instr); + if(flush_test) { + std::cout<<d->o_instr<<' '<<next_exp_insn<<"<<"<<'\n'; + flush_rand_delay_by = sim_time + (rand()%6); + assert(d->o_instr == next_exp_insn || next_exp_insn < 0); + } + } + + if(flush_test && sim_time <= flush_rand_delay_by) { + flush_rand_delay_by = 0; + d->i_flush = rand()%2; + d->i_exec_pc = rand()%4; + if(d->i_flush && first_flush) { + first_flush = false; + d->i_exec_pc = 3; + } + if(d->i_flush) { + next_exp_insn = instructions[d->i_exec_pc]; + } else { + next_exp_insn = exp_by_idx[d->rootp->fetch__DOT__fetch_pc]; + } + std::cout<<"flush stat"<<(bool)d->i_flush<<"x"<<d->i_exec_pc<<'\n'; + } + + if(!d->i_next_ready) + std::cout<<"(NR)"; +} + +void tick(Vfetch *dut, VerilatedVcdC* vcd) { + do { + dut->i_clk ^= 1; + dut->eval(); + vcd->dump(sim_time++); + } while(dut->i_clk); + + std::cout<<"."; +} + +void cpu_reset(Vfetch* dut, VerilatedVcdC* v_vcd) { + dut->i_rst = 1; + for(int i=0; i<4; i++) + tick(dut, v_vcd); + dut->i_rst = 0; + req_time = 0; +} + +void test_run_flush(Vfetch *dut, VerilatedVcdC* vcd) { + cpu_reset(dut, vcd); + res_ins.clear(); + while (sim_time < MAX_SIM_TIME*2) { + // simulate external devices + sim_mem(dut); + sim_cpu(dut, true); + + dut->i_next_ready = rand()%2; + + // clock tick + tick(dut, vcd); + dut->i_flush = 0; // one cycle signal + + if(next_exp_insn == -1) + break; + } + + std::cout<<"\nfetch flush_test passed\n"; +} + +int main() { + Vfetch *dut = new Vfetch; + + Verilated::traceEverOn(true); + VerilatedVcdC *v_vcd = new VerilatedVcdC; + dut->trace(v_vcd, 99); + v_vcd->open("waveform.vcd"); + + cpu_reset(dut, v_vcd); + + srand(1); + dut->i_next_ready = 1; + dut->i_flush = 0; + + for(int r=0; r<3; r++) { + cpu_reset(dut, v_vcd); + res_ins.clear(); + while (sim_time < MAX_SIM_TIME) { + // simulate external devices + sim_mem(dut); + sim_cpu(dut); + + if(r >= 1) + dut->i_next_ready = rand()%2; + else + dut->i_next_ready = 1; + + // clock tick + tick(dut, v_vcd); + + if(res_ins.size() == exp_ins.size()) + break; + } + // verify + for(int i=0; i<exp_ins.size(); i++) { + assert(res_ins.size() > i); + assert(res_ins[i] == exp_ins[i]); + } + + std::cout<<"\nfetch test run"<<r<<"passed\n"; + } + + test_run_flush(dut, v_vcd); + + dut->final(); + v_vcd->close(); + delete dut; + + std::cout<<'\n'; +}
diff --git a/verilog/rtl/ppcpu/test/tb_icache.cpp b/verilog/rtl/ppcpu/test/tb_icache.cpp new file mode 100644 index 0000000..17f7b4c --- /dev/null +++ b/verilog/rtl/ppcpu/test/tb_icache.cpp
@@ -0,0 +1,211 @@ + +#include "Vicache.h" +#include "Vicache___024root.h" + +#include <iostream> +#include <verilated_vcd_c.h> +#include <verilated.h> + +typedef Vicache Vdut; + +uint64_t sim_time = 0; + +int req_time = 0; +int req_addr = 0; +int burst_num = 0; + +void settle(Vdut *d) { + d->i_clk = 0; + d->eval(); +} + +void tick(Vdut *d, VerilatedVcdC* vcd) { + sim_time++; + + settle(d); + vcd->dump(sim_time*10-1); + + d->i_clk = 1; + d->eval(); + vcd->dump(sim_time*10); + + d->i_clk = 0; + d->eval(); + vcd->dump(sim_time*10+5); + + vcd->flush(); + std::cout<<"."; +} + +void cpu_reset(Vdut* dut, VerilatedVcdC* v_vcd) { + dut->i_rst = 1; + for(int i=0; i<4; i++) + tick(dut, v_vcd); + dut->i_rst = 0; + req_time = 0; +} + +void sim_mem(Vdut* d) { + d->omem_ack = 0; + if(d->omem_req) { + if(!req_time) { + req_time = (rand()%4)+1; + req_addr = d->omem_addr; + burst_num = 0; + } + + if(--req_time <= 0) { + assert(d->omem_burst4); + req_time = (rand()%2)+1; + d->omem_data = req_addr+burst_num; + d->omem_ack = 1; + std::cout<<"reqf"<<req_addr+burst_num<<'\n'; + if(++burst_num >= 4) { + burst_num = 0; + req_time = 0; + } + } + } +} + +int m_req_addr = 3; +int reqstime; +int main() { + Vdut *d = new Vdut; + + Verilated::traceEverOn(true); + VerilatedVcdC *vcd = new VerilatedVcdC; + d->trace(vcd, 99); + vcd->open("waveform.vcd"); + + srand(5); + + cpu_reset(d, vcd); + + + tick(d, vcd); + for(int i=0; i<10000; i++) { + bool first_cyc = false; + if(d->imem_ack) { + + bool should_hit = (bool)(m_req_addr & 0x3); + std::cout<<d->imem_data<<' '<<m_req_addr<<'\n'; + std::cout<<should_hit<<' '<<((bool)d->rootp->icache__DOT__cache_hit)<<'s'<<'\n'; + assert(d->imem_data == m_req_addr); + if(should_hit) + assert(sim_time-reqstime <= 4); + else + assert(sim_time-reqstime > 4); + + if(d->imem_next) { + first_cyc = true; + d->imem_req = 1; + d->imem_addr = ++m_req_addr; + reqstime = sim_time; + } else { + d->imem_req = 0; + } + + d->imem_next = 0; + } + + settle(d); + + if(d->imem_req && !d->imem_next && (rand()&2) && !first_cyc) { + d->imem_next = 1; + d->imem_addr = m_req_addr+1; + } + + d->imem_sync_next = 0; + if(!d->imem_req && (rand()&1)) { + first_cyc = true; + d->imem_next = 0; + d->imem_sync_next = d->imem_ack; + d->imem_addr = ++m_req_addr; + d->imem_req = 1; + reqstime = sim_time; + } + + tick(d, vcd); + sim_mem(d); + settle(d); + } + + d->imem_req = 0; + d->imem_next = 0; + cpu_reset(d, vcd); + tick(d, vcd); + d->imem_req = 1; + d->imem_next = 0; + d->imem_addr = 4; + do { + tick(d, vcd); + sim_mem(d); + settle(d); + d->imem_next = 1; + d->imem_sync_next = 1; + d->imem_addr = 5; + } while(!d->imem_ack); + int req[] = {5, 6, 7, 4, 5}; + for(int i=0; i<5; i++) { + d->imem_req = 1; + d->imem_next = 1; + d->imem_sync_next = 1; + d->imem_addr = req[i]; + tick(d, vcd); + sim_mem(d); + settle(d); + if(i!=0) // delay after cache update + assert(d->imem_ack); + } + d->imem_req = 1; + d->imem_next = 0; + d->imem_sync_next = 1; + d->imem_addr = 8; + tick(d, vcd); + sim_mem(d); + settle(d); + do { + tick(d, vcd); + sim_mem(d); + settle(d); + d->imem_next = 1; + d->imem_sync_next = 1; + d->imem_addr = 9; + } while(!d->imem_ack); + for(int i=0; i<2; i++) { + d->imem_req = 0; + tick(d, vcd); + sim_mem(d); + settle(d); + } + + int req2[] = {9, 6, 7, 8}; + for(int i=0; i<4; i++) { + d->imem_req = 1; + d->imem_next = 1; + d->imem_sync_next = 1; + d->imem_addr = req2[i]; + tick(d, vcd); + sim_mem(d); + settle(d); + if(i!=0) // delay after cache update + assert(d->imem_ack); + } + + for(int i=0; i<2; i++) { + d->imem_req = 0; + tick(d, vcd); + sim_mem(d); + settle(d); + if(!i) + assert(d->imem_ack); + } + + d->final(); + vcd->close(); + delete d; + + std::cout<<"\nALL TESTS PASSED\n\n"; + return 0; +}
diff --git a/verilog/rtl/ppcpu/test/tb_memwb.cpp b/verilog/rtl/ppcpu/test/tb_memwb.cpp new file mode 100644 index 0000000..cbf7d98 --- /dev/null +++ b/verilog/rtl/ppcpu/test/tb_memwb.cpp
@@ -0,0 +1,140 @@ +#include "Vmemwb.h" + +#include <iostream> +#include <verilated_vcd_c.h> +#include <verilated.h> + +#define MAX_SIM_TIME 200 +uint64_t sim_time = 0; + +int req_time = 0; +int i_seq = 1; + +int mem_cell = 0; + +void sim_mem(Vmemwb* d) { + if(d->i_clk) + return; + + d->i_mem_ack = 0; + if(d->o_mem_req && !d->i_mem_ack) { + static int req_data = -1, req_we = -1, req_addr = -1; + if(!req_time) { + req_addr = d->o_mem_addr; + req_data = d->o_mem_data; + req_we = d->o_mem_we; + } + + if(++req_time >= rand()%3) { + req_time = 0; + d->i_mem_ack = 1; + assert(req_addr == 10); + if (req_we) { + mem_cell = req_data; // in reality state would be busy for more cycles, but here + // we simulate that by delaying ack on random requests + } else { + d->i_mem_data = mem_cell; + } + std::cout<<"REQF "<<req_addr<<" "<<req_data<<" "<<req_we<<" "<<mem_cell<<"\n"; + } + } +} + +void tick(Vmemwb *dut, VerilatedVcdC* vcd) { + do { + dut->i_clk ^= 1; + dut->eval(); + vcd->dump(sim_time++); + } while(dut->i_clk); + + std::cout<<"."; + sim_mem(dut); + + if(sim_time > MAX_SIM_TIME) { + dut->final(); + vcd->close(); + delete dut; + assert(false); + } +} + +void cpu_reset(Vmemwb* dut, VerilatedVcdC* v_vcd) { + dut->i_rst = 1; + for(int i=0; i<4; i++) + tick(dut, v_vcd); + dut->i_rst = 0; +} + +void submit_request(Vmemwb* dut, VerilatedVcdC* vcd, int addr, int reg, int data, bool we, bool mem) { + std::cout<<"submit"; + while (!dut->o_ready) + tick(dut, vcd); + + dut->i_addr = addr; + dut->i_reg_ie = ((mem && !we) || (!mem && we) ? (1<<reg) : 0); + dut->i_data = data; + dut->i_mem_access = mem; + dut->i_mem_we = we && mem; + dut->i_submit = 1; + tick(dut, vcd); + dut->i_submit = 0; +} + +bool test_inc (Vmemwb* dut, VerilatedVcdC* vcd) { + cpu_reset(dut, vcd); + + submit_request(dut, vcd, 0, 3, 1, 1, 0); + assert(dut->o_reg_ie == 0x8); + assert(dut->o_reg_data == 0x1); + assert(dut->o_ready); + assert(!dut->o_mem_req); + submit_request(dut, vcd, 0, 3, 2, 1, 0); + assert(dut->o_reg_ie == 0x8); + assert(dut->o_reg_data == 0x2); + assert(dut->o_ready); + assert(!dut->o_mem_req); + + for(int i=0; i<10; i++) { + int data = rand() % 65536; + submit_request(dut, vcd, 10, 0, data, 1, 1); + assert(!dut->o_reg_ie); + submit_request(dut, vcd, 10, 2, 0, 0, 1); + while(!dut->o_reg_ie) + tick(dut, vcd); + assert(dut->o_reg_ie == 0x4); + assert(dut->o_reg_data == data); + assert(dut->o_ready); + } + + submit_request(dut, vcd, 10, 2, 0, 0, 0); + assert(!dut->o_reg_ie); + assert(dut->o_ready); + + return true; +} + +int main() { + Vmemwb *dut = new Vmemwb; + + Verilated::traceEverOn(true); + VerilatedVcdC *v_vcd = new VerilatedVcdC; + dut->trace(v_vcd, 99); + v_vcd->open("waveform.vcd"); + + bool fail = false; + srand(3); + + fail |= !test_inc(dut, v_vcd); + + dut->final(); + v_vcd->close(); + delete dut; + + std::cout<<'\n'; + if(fail) + std::cout<<"SOME TESTS FAILED\n"; + else + std::cout<<"ALL TESTS PASSED\n"; + std::cout<<'\n'; + return fail; +}
diff --git a/verilog/rtl/ppcpu/test/tb_upper_core.cpp b/verilog/rtl/ppcpu/test/tb_upper_core.cpp new file mode 100644 index 0000000..2520e2f --- /dev/null +++ b/verilog/rtl/ppcpu/test/tb_upper_core.cpp
@@ -0,0 +1,171 @@ +#include "Vupper_core.h" +#include "Vupper_core___024root.h" + +#include <iostream> +#include <verilated_vcd_c.h> +#include <verilated.h> + +#define MAX_TEST_TIME 200 +uint64_t sim_time = 0; +uint64_t test_time = 0; + +int req_time = 0; + +typedef Vupper_core Vdut; + +std::map <int, unsigned short> mem; + +void tick(Vdut *dut, VerilatedVcdC* vcd) { + do { + dut->i_clk ^= 1; + dut->eval(); + vcd->dump(sim_time++); + } while(dut->i_clk); + + std::cout<<"."; +} + +void cpu_reset(Vdut* dut, VerilatedVcdC* v_vcd) { + dut->i_rst = 1; + for(int i=0; i<4; i++) + tick(dut, v_vcd); + dut->i_rst = 0; + req_time = 0; + mem.clear(); +} + +void sim_wishbone_mem(Vdut* d) { + if(d->wb_cyc && d->wb_stb && !d->wb_ack) { + if (!req_time) { + std::cout<<"newmemreq"<<'\n'; + req_time = (rand()%3)+1; + } + + if(--req_time <= 0) { + d->wb_ack = 1; + if(d->wb_we) { + mem[d->wb_adr] = d->wb_o_dat; + } else { + d->wb_i_dat = mem[d->wb_adr]; + } + req_time = 0; + std::cout<<"memreqf "<<std::hex<<d->wb_adr<<' '<<(int)d->wb_we<<' '<<(d->wb_we ? d->wb_o_dat : d->wb_i_dat)<<'\n'; + } else { + d->wb_ack = 0; + } + } else { + d->wb_ack = 0; + } +} + +void load_bootjump() { + // disables mem paging and jumps to 0 (0x800000) + mem[0xffe000] = 0x0004; + mem[0xffe001] = 0x0000; + mem[0xffe002] = 0x0011; + mem[0xffe003] = 0x0002; + mem[0xffe004] = 0x000E; + mem[0xffe005] = 0x0000; +} + +void load_mem(std::vector<int>& instr) { + load_bootjump(); + for(int i=0; i<instr.size(); i++) { + mem[0x800000 + 2*i] = instr[i] & 0xffff; + mem[0x800000 + 2*i + 1] = instr[i] >> 16; + } +} + +bool test_interupts(Vdut* dut, VerilatedVcdC* vcd) { + // This test is executed as currenly the most complex one + // take listing from tb_core.cpp + std::vector <int> instr = { + 0x0002000E, + 0x0011000E, + 0x00000084, + 0x00050004, + 0x00010011, + 0x00000004, + 0x00100104, + 0x00010008, + 0x00020005, + 0x03840004, + 0x00020002, + 0x0000000D, + 0x000E030E, + 0x03200004, + 0x0000400C, + 0x0007018E, + 0x0019000E, + 0x00040190, + 0x00010488, + 0x00040C11, + 0x00000000, + 0x0000001E, + 0x00000000, + 0x00050004, + 0x01000011, + 0x0019000E}; + cpu_reset(dut, vcd); + load_mem(instr); + + test_time = sim_time; + int irqc = 0; + while (sim_time-test_time < MAX_TEST_TIME*200) { + sim_wishbone_mem(dut); + + if(rand() % 100 < 5 && !dut->i_irq && dut->dbg_pc != 0x14) { + std::cout<<"irq\n"; + irqc++; + dut->i_irq = 1; + } + + tick(dut, vcd); + + if(dut->dbg_pc == 0x14) { + std::cout<<"cli\n"; + dut->i_irq = 0; // cli + } + + if(dut->dbg_pc >= instr.size()-1) { + std::cout<<"end"; + break; + } + } + + int r0 = dut->dbg_r0; + int r1 = dut->rootp->upper_core__DOT__core__DOT__execute__DOT__rf__DOT____Vcellout__rf_regs__BRA__1__KET____DOT__rf_reg__o_d; + std::cout<<"r0: "<<r0<<" r1: "<<r1<<" ex:"<<irqc<<'\n'; + if(r0 != 0x10 || r1 != irqc) { + std::cout<<"test_interrupt failed\n"; + return false; + } + std::cout<<"test_interrupt passed\n"; + return true; +} + +int main() { + Vdut *dut = new Vdut; + + Verilated::traceEverOn(true); + VerilatedVcdC *v_vcd = new VerilatedVcdC; + dut->trace(v_vcd, 99); + v_vcd->open("waveform.vcd"); + + bool fail = false; + srand(3); + + fail |= !test_interupts(dut, v_vcd); + + dut->final(); + v_vcd->close(); + delete dut; + + std::cout<<'\n'; + if(fail) + std::cout<<"SOME TESTS FAILED\n"; + else + std::cout<<"ALL TESTS PASSED\n"; + std::cout<<'\n'; + return fail; +}
diff --git a/verilog/rtl/ppcpu/test/upasm/run b/verilog/rtl/ppcpu/test/upasm/run new file mode 100755 index 0000000..07ae395 --- /dev/null +++ b/verilog/rtl/ppcpu/test/upasm/run
@@ -0,0 +1,5 @@ +verilator --cc -I../../rtl/ --exe --trace upper_core.v upcore_asm.cpp && \ +make -C obj_dir/ -f Vupper_core.mk Vupper_core && \ +cp obj_dir/Vupper_core archu && \ +python3 ~/opt/pcpu-tools/pas.py -v $1 && \ +./archu < out.hex \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/test/upasm/test_stdmem.s b/verilog/rtl/ppcpu/test/upasm/test_stdmem.s new file mode 100644 index 0000000..841e599 --- /dev/null +++ b/verilog/rtl/ppcpu/test/upasm/test_stdmem.s
@@ -0,0 +1,39 @@ +ldi r0, 0x1234 +std r0, 0 +sd8 r0, 2 +ldi r0, 0 + +ld8 r0, 0 +cmi r0, 0x34 +jne err + +ld8 r0, 1 +cmi r0, 0x12 +jne err + +ldd r0, 0 +cmi r0, 0x1234 +jne err + +ldi r0, 0xff +sd8 r0, 1 + +ldi r0, 0 +ldd r0, 0 +cmi r0, 0xff34 +jne err + +ldi r0, 0xee +sd8 r0, 0 +ldd r0, 0 +cmi r0, 0xffee +jne err + +jmp pass + +err: +jmp err + +.org 0x400 +pass: +jmp pass \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/test/upasm/test_sys_and_pg.s b/verilog/rtl/ppcpu/test/upasm/test_sys_and_pg.s new file mode 100644 index 0000000..1ed1368 --- /dev/null +++ b/verilog/rtl/ppcpu/test/upasm/test_sys_and_pg.s
@@ -0,0 +1,81 @@ +ref0: +; resetv +jmp page0 +; intv +jmp inth + +page0: +; init counters +ldi r5, 0 +ldi r6, 0 +ldi r7, 0 + +; setup page table for 0->page1 +ldi r0, 1 +srs r0, 0x100 + +; init exec info +ldi r1, 0 ; p1 +ldi r2, 0 ; cnt 0 + +; enable instr paging +ldi r0, 1 +srs r0, 2 +jmp ref0 + +jmp err ; ASSERT_NOT_REACHED + +inth: +adi r2, r2, 1 +cmi r2, 100 +jne pend_skip +cmi r1, 1 +jeq final +ldi r1, 1 +ldi r2, 0 +pend_skip: +cmi r1, 0 +jeq lpl_skip +; load p2 paging +ldi r0, 2 +srs r0, 0x100 +lpl_skip: +; reenable paging +ldi r0, 1 +srs r0, 2 +jmp ref0 + +jmp err ; ASSERT_NOT_REACHED + + +final: +cmi r5, 300 +jne err +cmi r6, 100 +jne err +cmi r7, 100 +jne err +jmp pass + +err: +jmp err + +.org 0x400 +pass: +jmp pass + +.org 0x800 +page1: +adi r5, r5, 1 +adi r6, r6, 1 +sys +errp1: +jmp errp1 ; ASSERT_NOT_REACHED + +.org 0x1000 +page2: +adi r5, r5, 2 +adi r7, r7, 1 +sys +errp2: +jmp errp2 ; ASSERT_NOT_REACHED \ No newline at end of file
diff --git a/verilog/rtl/ppcpu/test/upasm/upcore_asm.cpp b/verilog/rtl/ppcpu/test/upasm/upcore_asm.cpp new file mode 100644 index 0000000..b8d7809 --- /dev/null +++ b/verilog/rtl/ppcpu/test/upasm/upcore_asm.cpp
@@ -0,0 +1,160 @@ +#include "Vupper_core.h" +#include "Vupper_core___024root.h" + +#include <iostream> +#include <verilated_vcd_c.h> +#include <verilated.h> + +#define MAX_TEST_TIME 200000 +uint64_t sim_time = 0; +uint64_t test_time = 0; + +#define PASS_MAGIC 0x400 + +int req_time = 0; + +typedef Vupper_core Vdut; + +std::map <int, unsigned short> mem; + +void tick(Vdut *dut, VerilatedVcdC* vcd) { + do { + dut->i_clk ^= 1; + dut->eval(); + vcd->dump(sim_time++); + } while(dut->i_clk); + + std::cout<<"."; +} + +void cpu_reset(Vdut* dut, VerilatedVcdC* v_vcd) { + dut->i_rst = 1; + for(int i=0; i<4; i++) + tick(dut, v_vcd); + dut->i_rst = 0; + req_time = 0; + mem.clear(); +} + +void sim_wishbone_mem(Vdut* d) { + if(d->wb_cyc && d->wb_stb && !d->wb_ack) { + if (!req_time) { + req_time = (rand()%3)+1; + } + + if(--req_time <= 0) { + d->wb_ack = 1; + if(d->wb_we) { + if(d->wb_sel & 1) { + mem[d->wb_adr] &= 0xff00u; + mem[d->wb_adr] |= d->wb_o_dat & 0xffu; + } + if(d->wb_sel & 2) { + mem[d->wb_adr] &= 0xffu; + mem[d->wb_adr] |= d->wb_o_dat & 0xff00u; + } + } else { + d->wb_i_dat = mem[d->wb_adr]; + } + req_time = 0; + std::cout<<std::hex<<d->wb_adr<<' '; + } else { + d->wb_ack = 0; + } + } else { + d->wb_ack = 0; + } +} + +void load_bootjump() { + // disables mem paging and jumps to 0 (0x800000) + mem[0xffe000] = 0x0004; + mem[0xffe001] = 0x0000; + mem[0xffe002] = 0x0011; + mem[0xffe003] = 0x0002; + mem[0xffe004] = 0x000E; + mem[0xffe005] = 0x0000; +} + +void load_mem(std::vector<int>& instr) { + load_bootjump(); + for(int i=0; i<instr.size(); i++) { + mem[0x800000 + 2*i] = instr[i] & 0xffff; + mem[0x800000 + 2*i + 1] = instr[i] >> 16; + } +} + +void load_hex() { + load_bootjump(); + std::string line; + while(std::cin>>line) { + if(line == ":00000001FF") + break; + + if(line.size() != 19 || line[0] != ':') { + std::cerr<<"invalid input"; + exit(1); + } + + std::string addr = line.substr(3, 4); + std::string data = line.substr(9, 8); + + mem[0x800000 | (std::stoul(addr, nullptr, 16)<<1)] = std::stoul(data, nullptr, 16) & 0xffff; + mem[0x800000 | ((std::stoul(addr, nullptr, 16)<<1)+1)] = std::stoul(data, nullptr, 16) >> 16; + std::cout<<std::hex<<(0x800000 | (std::stoul(addr, nullptr, 16)<<1))<<" to "<<(std::stoul(data, nullptr, 16) & 0xffff)<<'\n'; + } +} + +#define RF_REG(x) dut->rootp->upper_core__DOT__core__DOT__execute__DOT__rf__DOT____Vcellout__rf_regs__BRA__ ## x ## __KET____DOT__rf_reg__o_d + +int main() { + Vdut *dut = new Vdut; + + Verilated::traceEverOn(true); + VerilatedVcdC *vcd = new VerilatedVcdC; + dut->trace(vcd, 99); + vcd->open("waveform.vcd"); + + srand(8); + cpu_reset(dut, vcd); + + load_hex(); + int prev_pc = -1; + bool fail = true; + while (sim_time <= MAX_TEST_TIME) { + sim_wishbone_mem(dut); + + tick(dut, vcd); + + if(dut->dbg_pc != prev_pc) { + prev_pc = dut->dbg_pc; + std::cout<<'\n'; + std::cout<<" pc:"<<dut->dbg_pc<<' '; + std::cout<<"r"<<0<<": "<<std::hex<<RF_REG(0)<<' '; + std::cout<<"r"<<1<<": "<<std::hex<<RF_REG(1)<<' '; + std::cout<<"r"<<2<<": "<<std::hex<<RF_REG(2)<<' '; + std::cout<<"r"<<3<<": "<<std::hex<<RF_REG(3)<<' '; + std::cout<<"r"<<4<<": "<<std::hex<<RF_REG(4)<<' '; + std::cout<<"r"<<5<<": "<<std::hex<<RF_REG(5)<<' '; + std::cout<<"r"<<6<<": "<<std::hex<<RF_REG(6)<<' '; + std::cout<<"r"<<7<<": "<<std::hex<<RF_REG(7)<<' '; + } + + if(dut->dbg_pc == PASS_MAGIC) { + fail = false; + break; + } + } + + dut->final(); + vcd->close(); + delete dut; + + std::cout<<'\n'; + if(fail) + std::cout<<"TEST FAILED\n"; + else + std::cout<<"TEST PASSED\n"; + std::cout<<'\n'; + return fail; +}
diff --git a/verilog/rtl/ppcpu/test/wishbone/tb_mb_downconverter.cpp b/verilog/rtl/ppcpu/test/wishbone/tb_mb_downconverter.cpp new file mode 100644 index 0000000..f42f79a --- /dev/null +++ b/verilog/rtl/ppcpu/test/wishbone/tb_mb_downconverter.cpp
@@ -0,0 +1,119 @@ +#include "Vmb_downconverter.h" + +#include <iostream> +#include <verilated_vcd_c.h> +#include <verilated.h> + +typedef Vmb_downconverter Vdut; + +uint64_t sim_time = 0; + +int req_time = 0; +int req_addr = 0; +std::map <int, int> exp_data; + +void settle(Vdut *d) { + d->i_clk = 0; + d->eval(); +} + +void tick(Vdut *d, VerilatedVcdC* vcd) { + sim_time++; + + settle(d); + vcd->dump(sim_time*10-1); + + d->i_clk = 1; + d->eval(); + vcd->dump(sim_time*10); + + d->i_clk = 0; + d->eval(); + vcd->dump(sim_time*10+5); + + vcd->flush(); + + std::cout<<"."; +} + +void cpu_reset(Vdut* dut, VerilatedVcdC* v_vcd) { + dut->i_rst = 1; + for(int i=0; i<4; i++) + tick(dut, v_vcd); + dut->i_rst = 0; +} + +void sim_new_mem(Vdut* d) { + d->d_req_data_valid = 0; + if(d->d_req_active && (!d->d_req_data_valid || d->d_req_next)) { + if (!req_time) { + req_addr = d->d_req_addr; + std::cout<<std::hex<<"new"<<' '<<req_addr<<'\n'; + } + + if(++req_time >= rand()%8) { + req_time = 0; + d->d_req_data_valid = 1; + + if(!exp_data.count(req_addr)) + exp_data[req_addr] = rand()%65535; + + d->d_req_data = exp_data[req_addr]; + std::cout<<std::hex<<"completed"<<' '<<req_addr<<' '<<d->d_req_data<<'\n'; + + if(d->d_req_next && d->d_req_active) { + req_addr = d->d_req_addr; + req_time = 1; + std::cout<<"next"<<req_addr; + } + } + } +} + + +int main() { + Vdut *d = new Vdut; + + Verilated::traceEverOn(true); + VerilatedVcdC *vcd = new VerilatedVcdC; + d->trace(vcd, 99); + vcd->open("waveform.vcd"); + + srand(5); + + cpu_reset(d, vcd); + + bool fail = false; + for(int i=0; i<100; i++) { + if(!d->u_req_active && !(rand() % 3)) { + d->u_req_active = 1; + d->u_req_addr = rand()%30000; + } + + settle(d); + sim_new_mem(d); + + settle(d); + if(d->u_req_data_valid) { + d->u_req_active = 0; + int req_addr = (d->u_req_addr<<1); + long long exp_instr = (exp_data[req_addr]) | ((long long) exp_data[req_addr+1]<<16ll); + std::cout<<std::hex<<d->u_req_data<<' '<<exp_instr<<' '<<exp_data[req_addr]<<' '<<exp_data[req_addr+1]<<'\n'; + if(d->u_req_data != exp_instr) { + std::cout<<"test failed\n"; + fail = true; + break; + } + } + + tick(d, vcd); + } + + d->final(); + vcd->close(); + delete d; + + if(!fail) + std::cout<<"\nALL TESTS PASSED\n\n"; + return fail; +}
diff --git a/verilog/rtl/ppcpu/test/wishbone/tb_wishbone_arbiter.cpp b/verilog/rtl/ppcpu/test/wishbone/tb_wishbone_arbiter.cpp new file mode 100644 index 0000000..c9c7b05 --- /dev/null +++ b/verilog/rtl/ppcpu/test/wishbone/tb_wishbone_arbiter.cpp
@@ -0,0 +1,64 @@ +#include "Vwishbone_arbiter.h" + +#include <iostream> +#include <verilated_vcd_c.h> +#include <verilated.h> + +typedef Vwishbone_arbiter Vdut; + +uint64_t sim_time = 0; + +void tick(Vdut *dut, VerilatedVcdC* vcd) { + do { + dut->i_clk ^= 1; + dut->eval(); + vcd->dump(sim_time++); + } while(dut->i_clk); + + std::cout<<"."; +} + +void cpu_reset(Vdut* dut, VerilatedVcdC* v_vcd) { + dut->i_rst = 1; + for(int i=0; i<4; i++) + tick(dut, v_vcd); + dut->i_rst = 0; +} + +int main() { + Vdut *dut = new Vdut; + + Verilated::traceEverOn(true); + VerilatedVcdC *vcd = new VerilatedVcdC; + dut->trace(vcd, 99); + vcd->open("waveform.vcd"); + + srand(4); + + cpu_reset(dut, vcd); + bool prev_cyc = false; + for(int i=0; i<100; i++) { + int cyc0 = rand()&1; + int cyc1 = rand()&1; + dut->i_wb0_cyc = cyc0; + dut->i_wb1_cyc = cyc1; + tick(dut, vcd); + + if(dut->o_wb_cyc) { // granted + if(!dut->o_sel_sig) + assert(dut->i_wb0_cyc); + else if(!prev_cyc) + assert(dut->i_wb1_cyc && !dut->i_wb0_cyc); + else + assert(dut->i_wb1_cyc); + } + prev_cyc = dut->o_wb_cyc; + } + + dut->final(); + vcd->close(); + delete dut; + + std::cout<<"\nALL TESTS PASSED\n\n"; + return 0; +}
diff --git a/verilog/rtl/ppcpu/test/wishbone/tb_wishbone_master.cpp b/verilog/rtl/ppcpu/test/wishbone/tb_wishbone_master.cpp new file mode 100644 index 0000000..a7e5360 --- /dev/null +++ b/verilog/rtl/ppcpu/test/wishbone/tb_wishbone_master.cpp
@@ -0,0 +1,145 @@ +#include "Vwishbone_master.h" + +#include <iostream> +#include <verilated_vcd_c.h> +#include <verilated.h> + +#define MAX_TEST_TIME 200 +uint64_t sim_time = 0; + +void tick(Vwishbone_master *dut, VerilatedVcdC* vcd) { + do { + dut->i_clk ^= 1; + dut->eval(); + vcd->dump(sim_time++); + } while(dut->i_clk); + + std::cout<<"."; +} + +void cpu_reset(Vwishbone_master* dut, VerilatedVcdC* v_vcd) { + dut->i_rst = 1; + for(int i=0; i<4; i++) + tick(dut, v_vcd); + dut->i_rst = 0; +} + +void do_request(Vwishbone_master* dut, VerilatedVcdC* vcd, int addr, int data, bool we) { + dut->i_mem_addr = addr; + dut->i_mem_data = data; + dut->i_mem_we = we; + dut->i_mem_req = 1; + tick(dut, vcd); + dut->i_mem_req = 0; +} + +void wb_respond(Vwishbone_master* dut, VerilatedVcdC* vcd, int data) { + assert(dut->wb_cyc); + assert(dut->wb_stb); + dut->wb_ack = 1; + dut->wb_i_dat = data; + tick(dut, vcd); + dut->wb_ack = 0; +} + +bool test_timing(Vwishbone_master* dut, VerilatedVcdC* vcd) { + cpu_reset(dut, vcd); + + // check timing + do_request(dut, vcd, 1, 0, 0); + assert(dut->wb_cyc); + assert(dut->wb_stb); + assert(!dut->wb_we); + assert(dut->wb_adr == 1); + wb_respond(dut, vcd, 2); + assert(dut->o_mem_ack); + assert(dut->o_mem_data == 2); + + // test fetch_next + do_request(dut, vcd, 2, 0, 0); + dut->i_mem_next = 1; + dut->i_mem_addr = 3; + assert(dut->wb_adr == 2); + wb_respond(dut, vcd, 1); + dut->i_mem_next = 0; + assert(dut->wb_adr == 3); + assert(dut->o_mem_ack); + assert(dut->o_mem_data == 1); + wb_respond(dut, vcd, 2); + assert(dut->o_mem_ack); + assert(dut->o_mem_data == 2); + assert(!dut->wb_cyc); + tick(dut, vcd); + assert(!dut->o_mem_ack); + + std::cout<<"\ntest_wisbone_timing passed\n"; + return true; +} + +bool test_rand(Vwishbone_master* dut, VerilatedVcdC* vcd) { + cpu_reset(dut, vcd); + + int exp_adr, exp_n_adr = 0; + for(int i=0; i<100; i++) { + if(!dut->wb_cyc) { + exp_adr = rand()%65535; + do_request(dut, vcd, exp_adr, 0, 0); + if(!(rand()%10)) { + exp_n_adr = (rand()%65534)+1; + dut->i_mem_next = 1; + dut->i_mem_addr = exp_n_adr; + tick(dut, vcd); + } + } else { + while(rand()%2) + tick(dut, vcd); + + int data = rand()%65535; + assert(dut->wb_adr == exp_adr); + wb_respond(dut, vcd, data); + assert(dut->o_mem_ack); + assert(dut->o_mem_data == data); + if(exp_n_adr) { + dut->i_mem_next = 0; + while(rand()%2) + tick(dut, vcd); + int data = rand()%65535; + assert(dut->wb_adr == exp_n_adr); + wb_respond(dut, vcd, data); + assert(dut->o_mem_ack); + assert(dut->o_mem_data == data); + exp_n_adr = 0; + } + } + } + + std::cout<<"\ntest_wisbone_rand passed\n"; + return true; +} + +int main() { + Vwishbone_master *dut = new Vwishbone_master; + + Verilated::traceEverOn(true); + VerilatedVcdC *v_vcd = new VerilatedVcdC; + dut->trace(v_vcd, 99); + v_vcd->open("waveform.vcd"); + + bool fail = false; + srand(4); + + fail |= !test_timing(dut, v_vcd); + fail |= !test_rand(dut, v_vcd); + + dut->final(); + v_vcd->close(); + delete dut; + + std::cout<<'\n'; + if(fail) + std::cout<<"SOME TESTS FAILED\n"; + else + std::cout<<"ALL TESTS PASSED\n"; + std::cout<<'\n'; + return fail; +}