Added Pico Co-Processor Interface (PCPI)

This commit is contained in:
Clifford Wolf 2015-06-26 23:14:38 +02:00
parent d4331491a8
commit 0be990bd04
6 changed files with 318 additions and 14 deletions

View File

@ -1,6 +1,6 @@
TEST_OBJS = $(addsuffix .o,$(basename $(wildcard tests/*.S))) TEST_OBJS = $(addsuffix .o,$(basename $(wildcard tests/*.S)))
FIRMWARE_OBJS = firmware/start.o firmware/irq.o firmware/print.o firmware/sieve.o firmware/stats.o FIRMWARE_OBJS = firmware/start.o firmware/irq.o firmware/print.o firmware/sieve.o firmware/multest.o firmware/stats.o
test: testbench.exe firmware/firmware.hex test: testbench.exe firmware/firmware.hex
vvp -N testbench.exe vvp -N testbench.exe

View File

@ -16,6 +16,13 @@ void print_hex(unsigned int val);
// sieve.c // sieve.c
void sieve(); void sieve();
// multest.c
uint32_t hard_mul(uint32_t a, uint32_t b);
uint32_t hard_mulh(uint32_t a, uint32_t b);
uint32_t hard_mulhsu(uint32_t a, uint32_t b);
uint32_t hard_mulhu(uint32_t a, uint32_t b);
void multest();
// stats.c // stats.c
void stats(); void stats();

79
firmware/multest.c Normal file
View File

@ -0,0 +1,79 @@
#include "firmware.h"
uint32_t xorshift32() {
static uint32_t x = 314159265;
x ^= x << 13;
x ^= x >> 17;
x ^= x << 5;
return x;
}
void multest()
{
int i;
for (i = 0; i < 10; i++)
{
uint32_t a = xorshift32();
uint32_t b = xorshift32();
uint64_t au = a, bu = b;
int64_t as = (int32_t)a, bs = (int32_t)b;
print_str("input [");
print_hex(as >> 32);
print_str("] ");
print_hex(a);
print_str(" [");
print_hex(bs >> 32);
print_str("] ");
print_hex(b);
print_chr('\n');
uint32_t h_mul, h_mulh, h_mulhsu, h_mulhu;
print_str("hard ");
h_mul = hard_mul(a, b);
print_hex(h_mul);
print_str(" ");
h_mulh = hard_mulh(a, b);
print_hex(h_mulh);
print_str(" ");
h_mulhsu = hard_mulhsu(a, b);
print_hex(h_mulhsu);
print_str(" ");
h_mulhu = hard_mulhu(a, b);
print_hex(h_mulhu);
print_chr('\n');
uint32_t s_mul, s_mulh, s_mulhsu, s_mulhu;
print_str("soft ");
s_mul = a * b;
print_hex(s_mul);
print_str(" ");
s_mulh = (as * bs) >> 32;
print_hex(s_mulh);
print_str(" ");
s_mulhsu = (as * bu) >> 32;
print_hex(s_mulhsu);
print_str(" ");
s_mulhu = (au * bu) >> 32;
print_hex(s_mulhu);
print_str(" ");
if (s_mul != h_mul || s_mulh != h_mulh || s_mulhsu != h_mulhsu || s_mulhu != h_mulhu) {
print_str("ERROR!\n");
asm volatile ("sbreak");
return;
}
print_str(" OK\n");
}
}

View File

@ -1,6 +1,11 @@
.section .text .section .text
.global irq .global irq
.global sieve .global sieve
.global multest
.global hard_mul
.global hard_mulh
.global hard_mulhsu
.global hard_mulhu
.global stats .global stats
#define TEST(n) \ #define TEST(n) \
@ -220,6 +225,9 @@ start:
/* jump to sieve C code */ /* jump to sieve C code */
jal ra,sieve jal ra,sieve
/* jump to sieve C code */
jal ra,multest
/* jump to stats C code */ /* jump to stats C code */
jal ra,stats jal ra,stats
@ -239,3 +247,19 @@ start:
/* break */ /* break */
sbreak sbreak
hard_mul:
mul a0, a0, a1
ret
hard_mulh:
mulh a0, a0, a1
ret
hard_mulhsu:
mulhsu a0, a0, a1
ret
hard_mulhu:
mulhu a0, a0, a1
ret

View File

@ -30,6 +30,7 @@ module picorv32 #(
parameter [ 0:0] ENABLE_REGS_16_31 = 1, parameter [ 0:0] ENABLE_REGS_16_31 = 1,
parameter [ 0:0] ENABLE_REGS_DUALPORT = 1, parameter [ 0:0] ENABLE_REGS_DUALPORT = 1,
parameter [ 0:0] LATCHED_MEM_RDATA = 0, parameter [ 0:0] LATCHED_MEM_RDATA = 0,
parameter [ 0:0] ENABLE_PCPI = 0,
parameter [ 0:0] ENABLE_IRQ = 0, parameter [ 0:0] ENABLE_IRQ = 0,
parameter [31:0] MASKED_IRQ = 32'h 0000_0000, parameter [31:0] MASKED_IRQ = 32'h 0000_0000,
parameter [31:0] PROGADDR_RESET = 32'h 0000_0000, parameter [31:0] PROGADDR_RESET = 32'h 0000_0000,
@ -47,14 +48,25 @@ module picorv32 #(
output reg [ 3:0] mem_wstrb, output reg [ 3:0] mem_wstrb,
input [31:0] mem_rdata, input [31:0] mem_rdata,
// look-ahead interface // Look-Ahead Interface
output mem_la_read, output mem_la_read,
output mem_la_write, output mem_la_write,
output [31:0] mem_la_addr, output [31:0] mem_la_addr,
output reg [31:0] mem_la_wdata, output reg [31:0] mem_la_wdata,
output reg [ 3:0] mem_la_wstrb, output reg [ 3:0] mem_la_wstrb,
// IRQ interface // Pico Co-Processor Interface (PCPI)
output reg pcpi_insn_valid,
output reg [31:0] pcpi_insn,
output reg pcpi_rs1_valid,
output [31:0] pcpi_rs1,
output reg pcpi_rs2_valid,
output [31:0] pcpi_rs2,
input pcpi_rd_valid,
input [31:0] pcpi_rd,
input pcpi_ready,
// IRQ Interface
input [31:0] irq, input [31:0] irq,
output reg [31:0] eoi output reg [31:0] eoi
); );
@ -71,6 +83,9 @@ module picorv32 #(
reg [31:0] cpuregs [0:regfile_size-1]; reg [31:0] cpuregs [0:regfile_size-1];
reg [4:0] reg_sh; reg [4:0] reg_sh;
assign pcpi_rs1 = reg_op1;
assign pcpi_rs2 = reg_op2;
wire [31:0] next_pc; wire [31:0] next_pc;
reg irq_active; reg irq_active;
@ -318,6 +333,10 @@ module picorv32 #(
end end
if (decoder_trigger && !decoder_pseudo_trigger) begin if (decoder_trigger && !decoder_pseudo_trigger) begin
if (ENABLE_PCPI) begin
pcpi_insn <= mem_rdata_q;
end
instr_beq <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b000; instr_beq <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b000;
instr_bne <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b001; instr_bne <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b001;
instr_blt <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b100; instr_blt <= is_beq_bne_blt_bge_bltu_bgeu && mem_rdata_q[14:12] == 3'b100;
@ -437,6 +456,9 @@ module picorv32 #(
reg [31:0] current_pc; reg [31:0] current_pc;
assign next_pc = latched_store && latched_branch ? reg_out : reg_next_pc; assign next_pc = latched_store && latched_branch ? reg_out : reg_next_pc;
reg [7:0] pcpi_timeout_counter;
reg pcpi_timeout;
reg [31:0] next_irq_pending; reg [31:0] next_irq_pending;
reg do_waitirq; reg do_waitirq;
@ -489,6 +511,15 @@ module picorv32 #(
reg_alu_out <= alu_out; reg_alu_out <= alu_out;
if (ENABLE_PCPI) begin
if (pcpi_insn_valid) begin
if (pcpi_timeout_counter)
pcpi_timeout_counter <= pcpi_timeout_counter - 1;
end else
pcpi_timeout_counter <= ~0;
pcpi_timeout <= !pcpi_timeout_counter;
end
if (ENABLE_COUNTERS) if (ENABLE_COUNTERS)
count_cycle <= resetn ? count_cycle + 1 : 0; count_cycle <= resetn ? count_cycle + 1 : 0;
@ -520,6 +551,9 @@ module picorv32 #(
latched_is_lu <= 0; latched_is_lu <= 0;
latched_is_lh <= 0; latched_is_lh <= 0;
latched_is_lb <= 0; latched_is_lb <= 0;
pcpi_insn_valid <= 0;
pcpi_rs1_valid <= 0;
pcpi_rs2_valid <= 0;
irq_active <= 0; irq_active <= 0;
irq_mask <= ~0; irq_mask <= ~0;
next_irq_pending = 0; next_irq_pending = 0;
@ -567,6 +601,12 @@ module picorv32 #(
reg_pc <= current_pc; reg_pc <= current_pc;
reg_next_pc <= current_pc; reg_next_pc <= current_pc;
if (ENABLE_PCPI) begin
pcpi_insn_valid <= 0;
pcpi_rs1_valid <= 0;
pcpi_rs2_valid <= 0;
end
latched_store <= 0; latched_store <= 0;
latched_stalu <= 0; latched_stalu <= 0;
latched_branch <= 0; latched_branch <= 0;
@ -618,14 +658,42 @@ module picorv32 #(
$display("DECODE: 0x%08x %-0s", reg_pc, instruction ? instruction : "UNKNOWN"); $display("DECODE: 0x%08x %-0s", reg_pc, instruction ? instruction : "UNKNOWN");
`endif `endif
if (instr_trap) begin if (instr_trap) begin
if (ENABLE_PCPI) begin
pcpi_rs1_valid <= 1;
pcpi_insn_valid <= 1;
reg_op1 <= decoded_rs1 ? cpuregs[decoded_rs1] : 0;
if (ENABLE_REGS_DUALPORT) begin
pcpi_rs2_valid <= 1;
reg_sh <= decoded_rs2 ? cpuregs[decoded_rs2] : 0;
reg_op2 <= decoded_rs2 ? cpuregs[decoded_rs2] : 0;
if (pcpi_ready) begin
reg_out <= pcpi_rd;
latched_store <= pcpi_rd_valid;
cpu_state <= cpu_state_fetch;
end else
if (pcpi_timeout) begin
`ifdef DEBUG `ifdef DEBUG
$display("SBREAK OR UNSUPPORTED INSN AT 0x%08x", reg_pc); $display("SBREAK OR UNSUPPORTED INSN AT 0x%08x", reg_pc);
`endif `endif
if (ENABLE_IRQ && !irq_mask[irq_sbreak] && !irq_active) begin if (ENABLE_IRQ && !irq_mask[irq_sbreak] && !irq_active) begin
next_irq_pending[irq_sbreak] = 1; next_irq_pending[irq_sbreak] = 1;
cpu_state <= cpu_state_fetch; cpu_state <= cpu_state_fetch;
end else end else
cpu_state <= cpu_state_trap; cpu_state <= cpu_state_trap;
end
end else begin
cpu_state <= cpu_state_ld_rs2;
end
end else begin
`ifdef DEBUG
$display("SBREAK OR UNSUPPORTED INSN AT 0x%08x", reg_pc);
`endif
if (ENABLE_IRQ && !irq_mask[irq_sbreak] && !irq_active) begin
next_irq_pending[irq_sbreak] = 1;
cpu_state <= cpu_state_fetch;
end else
cpu_state <= cpu_state_trap;
end
end else end else
if (is_rdcycle_rdcycleh_rdinstr_rdinstrh) begin if (is_rdcycle_rdcycleh_rdinstr_rdinstrh) begin
(* parallel_case, full_case *) (* parallel_case, full_case *)
@ -718,6 +786,24 @@ module picorv32 #(
`endif `endif
reg_sh <= decoded_rs2 ? cpuregs[decoded_rs2] : 0; reg_sh <= decoded_rs2 ? cpuregs[decoded_rs2] : 0;
reg_op2 <= decoded_rs2 ? cpuregs[decoded_rs2] : 0; reg_op2 <= decoded_rs2 ? cpuregs[decoded_rs2] : 0;
if (ENABLE_PCPI && pcpi_insn_valid) begin
pcpi_rs2_valid <= 1;
if (pcpi_ready) begin
reg_out <= pcpi_rd;
latched_store <= pcpi_rd_valid;
cpu_state <= cpu_state_fetch;
end else
if (pcpi_timeout) begin
`ifdef DEBUG
$display("SBREAK OR UNSUPPORTED INSN AT 0x%08x", reg_pc);
`endif
if (ENABLE_IRQ && !irq_mask[irq_sbreak] && !irq_active) begin
next_irq_pending[irq_sbreak] = 1;
cpu_state <= cpu_state_fetch;
end else
cpu_state <= cpu_state_trap;
end
end else
if (is_sb_sh_sw) begin if (is_sb_sh_sw) begin
cpu_state <= cpu_state_stmem; cpu_state <= cpu_state_stmem;
mem_do_rinst <= 1; mem_do_rinst <= 1;
@ -874,6 +960,70 @@ module picorv32 #(
endmodule endmodule
/***************************************************************
* picorv32_pcpi_mul
***************************************************************/
module picorv32_pcpi_mul (
input clk, resetn,
input pcpi_insn_valid,
input [31:0] pcpi_insn,
input pcpi_rs1_valid,
input [31:0] pcpi_rs1,
input pcpi_rs2_valid,
input [31:0] pcpi_rs2,
output reg pcpi_rd_valid,
output reg [31:0] pcpi_rd,
output reg pcpi_ready
);
reg instr_mul, instr_mulh, instr_mulhsu, instr_mulhu;
wire instr_any_mul = |{instr_mul, instr_mulh, instr_mulhsu, instr_mulhu};
wire instr_any_mulh = |{instr_mulh, instr_mulhsu, instr_mulhu};
wire instr_rs1_signed = |{instr_mulh, instr_mulhsu};
wire instr_rs2_signed = |{instr_mulh};
always @(posedge clk) begin
instr_mul <= 0;
instr_mulh <= 0;
instr_mulhsu <= 0;
instr_mulhu <= 0;
if (pcpi_insn_valid && pcpi_insn[6:0] == 7'b0110011 && pcpi_insn[31:25] == 7'b0000001) begin
case (pcpi_insn[14:12])
3'b000: instr_mul <= 1;
3'b001: instr_mulh <= 1;
3'b010: instr_mulhsu <= 1;
3'b011: instr_mulhu <= 1;
endcase
end
end
// FIXME: This is just a behavioral model
reg [63:0] rs1, rs2;
always @(posedge clk) begin
pcpi_rd_valid <= 0;
pcpi_ready <= 0;
if (pcpi_rs1_valid && pcpi_rs2_valid && instr_any_mul) begin
if (instr_rs1_signed)
rs1 = $signed(pcpi_rs1);
else
rs1 = $unsigned(pcpi_rs1);
if (instr_rs2_signed)
rs2 = $signed(pcpi_rs2);
else
rs2 = $unsigned(pcpi_rs2);
pcpi_rd <= instr_any_mulh ? (rs1 * rs2) >> 32 : rs1 * rs2;
pcpi_rd_valid <= 1;
pcpi_ready <= 1;
end
end
endmodule
/*************************************************************** /***************************************************************
* picorv32_axi * picorv32_axi
***************************************************************/ ***************************************************************/
@ -882,6 +1032,7 @@ module picorv32_axi #(
parameter [ 0:0] ENABLE_COUNTERS = 1, parameter [ 0:0] ENABLE_COUNTERS = 1,
parameter [ 0:0] ENABLE_REGS_16_31 = 1, parameter [ 0:0] ENABLE_REGS_16_31 = 1,
parameter [ 0:0] ENABLE_REGS_DUALPORT = 1, parameter [ 0:0] ENABLE_REGS_DUALPORT = 1,
parameter [ 0:0] ENABLE_MUL = 0,
parameter [ 0:0] ENABLE_IRQ = 0, parameter [ 0:0] ENABLE_IRQ = 0,
parameter [31:0] MASKED_IRQ = 32'h 0000_0000, parameter [31:0] MASKED_IRQ = 32'h 0000_0000,
parameter [31:0] PROGADDR_RESET = 32'h 0000_0000, parameter [31:0] PROGADDR_RESET = 32'h 0000_0000,
@ -926,6 +1077,16 @@ module picorv32_axi #(
wire mem_ready; wire mem_ready;
wire [31:0] mem_rdata; wire [31:0] mem_rdata;
wire pcpi_insn_valid;
wire [31:0] pcpi_insn;
wire pcpi_rs1_valid;
wire [31:0] pcpi_rs1;
wire pcpi_rs2_valid;
wire [31:0] pcpi_rs2;
wire pcpi_rd_valid;
wire [31:0] pcpi_rd;
wire pcpi_ready;
picorv32_axi_adapter axi_adapter ( picorv32_axi_adapter axi_adapter (
.clk (clk ), .clk (clk ),
.resetn (resetn ), .resetn (resetn ),
@ -955,18 +1116,39 @@ module picorv32_axi #(
.mem_rdata (mem_rdata ) .mem_rdata (mem_rdata )
); );
generate if (ENABLE_MUL) begin
picorv32_pcpi_mul pcpi_mul (
.clk (clk ),
.resetn (resetn ),
.pcpi_insn_valid(pcpi_insn_valid),
.pcpi_insn (pcpi_insn ),
.pcpi_rs1_valid (pcpi_rs1_valid ),
.pcpi_rs1 (pcpi_rs1 ),
.pcpi_rs2_valid (pcpi_rs2_valid ),
.pcpi_rs2 (pcpi_rs2 ),
.pcpi_rd_valid (pcpi_rd_valid ),
.pcpi_rd (pcpi_rd ),
.pcpi_ready (pcpi_ready )
);
end else begin
assign pcpi_rd = 1'bx;
assign pcpi_ready = 0;
end endgenerate
picorv32 #( picorv32 #(
.ENABLE_COUNTERS (ENABLE_COUNTERS ), .ENABLE_COUNTERS (ENABLE_COUNTERS ),
.ENABLE_REGS_16_31 (ENABLE_REGS_16_31 ), .ENABLE_REGS_16_31 (ENABLE_REGS_16_31 ),
.ENABLE_REGS_DUALPORT(ENABLE_REGS_DUALPORT), .ENABLE_REGS_DUALPORT(ENABLE_REGS_DUALPORT),
.ENABLE_PCPI (ENABLE_MUL ),
.ENABLE_IRQ (ENABLE_IRQ ), .ENABLE_IRQ (ENABLE_IRQ ),
.MASKED_IRQ (MASKED_IRQ ), .MASKED_IRQ (MASKED_IRQ ),
.PROGADDR_RESET (PROGADDR_RESET ), .PROGADDR_RESET (PROGADDR_RESET ),
.PROGADDR_IRQ (PROGADDR_IRQ ) .PROGADDR_IRQ (PROGADDR_IRQ )
) picorv32_core ( ) picorv32_core (
.clk (clk ), .clk (clk ),
.resetn (resetn ), .resetn (resetn),
.trap (trap ), .trap (trap ),
.mem_valid(mem_valid), .mem_valid(mem_valid),
.mem_addr (mem_addr ), .mem_addr (mem_addr ),
.mem_wdata(mem_wdata), .mem_wdata(mem_wdata),
@ -974,8 +1156,19 @@ module picorv32_axi #(
.mem_instr(mem_instr), .mem_instr(mem_instr),
.mem_ready(mem_ready), .mem_ready(mem_ready),
.mem_rdata(mem_rdata), .mem_rdata(mem_rdata),
.irq (irq ),
.eoi (eoi ) .pcpi_insn_valid(pcpi_insn_valid),
.pcpi_insn (pcpi_insn ),
.pcpi_rs1_valid (pcpi_rs1_valid ),
.pcpi_rs1 (pcpi_rs1 ),
.pcpi_rs2_valid (pcpi_rs2_valid ),
.pcpi_rs2 (pcpi_rs2 ),
.pcpi_rd_valid (pcpi_rd_valid ),
.pcpi_rd (pcpi_rd ),
.pcpi_ready (pcpi_ready ),
.irq(irq),
.eoi(eoi)
); );
endmodule endmodule

View File

@ -45,6 +45,7 @@ module testbench;
reg [31:0] mem_axi_rdata; reg [31:0] mem_axi_rdata;
picorv32_axi #( picorv32_axi #(
.ENABLE_MUL(1),
.ENABLE_IRQ(1) .ENABLE_IRQ(1)
) uut ( ) uut (
.clk (clk ), .clk (clk ),