URL
https://opencores.org/ocsvn/zipcpu/zipcpu/trunk
Subversion Repositories zipcpu
Compare Revisions
- This comparison shows the changes necessary to convert path
/zipcpu/trunk/rtl
- from Rev 209 to Rev 205
- ↔ Reverse comparison
Rev 209 → Rev 205
/README.md
File deleted
/ex/wbarbiter.v
File deleted
/ex/fwb_slave.v
File deleted
/ex/busdelay.v
File deleted
/ex/fwb_master.v
File deleted
/ex/wbdblpriarb.v
File deleted
/ex/wbpriarbiter.v
File deleted
/ex/fwb_counter.v
File deleted
/core/iscachable.v
File deleted
/core/slowmpy.v
File deleted
/core/README.md
File deleted
/core/mpyop.v
File deleted
/core/cpuops.v
4,8 → 4,9
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// Purpose: This is the ZipCPU ALU function. It handles all of the |
// instruction opcodes 0-13. (14-15 are divide opcodes). |
// Purpose: This supports the instruction set reordering of operations |
// created by the second generation instruction set, as well as |
// the new operations of POPC (population count) and BREV (bit reversal). |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
13,7 → 14,7
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2019, Gisselquist Technology, LLC |
// Copyright (C) 2015-2017, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
36,53 → 37,37
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
// |
// |
`include "cpudefs.v" |
// |
module cpuops(i_clk,i_reset, i_stb, i_op, i_a, i_b, o_c, o_f, o_valid, |
module cpuops(i_clk,i_rst, i_ce, i_op, i_a, i_b, o_c, o_f, o_valid, |
o_busy); |
parameter IMPLEMENT_MPY = `OPT_MULTIPLY; |
parameter [0:0] OPT_SHIFTS = 1'b1; |
input wire i_clk, i_reset, i_stb; |
input wire [3:0] i_op; |
input wire [31:0] i_a, i_b; |
parameter IMPLEMENT_MPY = `OPT_MULTIPLY; |
input i_clk, i_rst, i_ce; |
input [3:0] i_op; |
input [31:0] i_a, i_b; |
output reg [31:0] o_c; |
output wire [3:0] o_f; |
output reg o_valid; |
output wire o_busy; |
|
genvar k; |
|
// Shift register pre-logic |
wire [32:0] w_lsr_result, w_asr_result, w_lsl_result; |
generate if (OPT_SHIFTS) |
begin : IMPLEMENT_SHIFTS |
wire signed [32:0] w_pre_asr_input, w_pre_asr_shifted; |
assign w_pre_asr_input = { i_a, 1'b0 }; |
assign w_pre_asr_shifted = w_pre_asr_input >>> i_b[4:0]; |
assign w_asr_result = (|i_b[31:5])? {(33){i_a[31]}} |
: w_pre_asr_shifted;// ASR |
assign w_lsr_result = ((|i_b[31:6])||(i_b[5]&&(i_b[4:0]!=0)))? 33'h00 |
:((i_b[5])?{32'h0,i_a[31]} |
wire signed [32:0] w_pre_asr_input, w_pre_asr_shifted; |
assign w_pre_asr_input = { i_a, 1'b0 }; |
assign w_pre_asr_shifted = w_pre_asr_input >>> i_b[4:0]; |
assign w_asr_result = (|i_b[31:5])? {(33){i_a[31]}} |
: w_pre_asr_shifted;// ASR |
assign w_lsr_result = ((|i_b[31:6])||(i_b[5]&&(i_b[4:0]!=0)))? 33'h00 |
:((i_b[5])?{32'h0,i_a[31]} |
|
: ( { i_a, 1'b0 } >> (i_b[4:0]) ));// LSR |
assign w_lsl_result = ((|i_b[31:6])||(i_b[5]&&(i_b[4:0]!=0)))? 33'h00 |
:((i_b[5])?{i_a[0], 32'h0} |
: ({1'b0, i_a } << i_b[4:0])); // LSL |
|
: ( { i_a, 1'b0 } >> (i_b[4:0]) ));// LSR |
assign w_lsl_result = ((|i_b[31:6])||(i_b[5]&&(i_b[4:0]!=0)))? 33'h00 |
:((i_b[5])?{i_a[0], 32'h0} |
: ({1'b0, i_a } << i_b[4:0])); // LSL |
end else begin : NO_SHIFTS |
|
assign w_asr_result = { i_a[31], i_a[31:0] }; |
assign w_lsr_result = { 1'b0, i_a[31:0] }; |
assign w_lsl_result = { i_a[31:0], 1'b0 }; |
|
end endgenerate |
|
// |
// Bit reversal pre-logic |
wire [31:0] w_brev_result; |
genvar k; |
generate |
for(k=0; k<32; k=k+1) |
begin : bit_reversal_cpuop |
93,48 → 78,241
wire z, n, v; |
reg c, pre_sign, set_ovfl, keep_sgn_on_ovfl; |
always @(posedge i_clk) |
if (i_stb) // 1 LUT |
set_ovfl<=(((i_op==4'h0)&&(i_a[31] != i_b[31]))//SUB&CMP |
||((i_op==4'h2)&&(i_a[31] == i_b[31])) // ADD |
||(i_op == 4'h6) // LSL |
||(i_op == 4'h5)); // LSR |
|
if (i_ce) // 1 LUT |
set_ovfl<=(((i_op==4'h0)&&(i_a[31] != i_b[31]))//SUB&CMP |
||((i_op==4'h2)&&(i_a[31] == i_b[31])) // ADD |
||(i_op == 4'h6) // LSL |
||(i_op == 4'h5)); // LSR |
always @(posedge i_clk) |
if (i_stb) // 1 LUT |
keep_sgn_on_ovfl<= |
(((i_op==4'h0)&&(i_a[31] != i_b[31]))//SUB&CMP |
||((i_op==4'h2)&&(i_a[31] == i_b[31]))); // ADD |
if (i_ce) // 1 LUT |
keep_sgn_on_ovfl<= |
(((i_op==4'h0)&&(i_a[31] != i_b[31]))//SUB&CMP |
||((i_op==4'h2)&&(i_a[31] == i_b[31]))); // ADD |
|
wire [63:0] mpy_result; // Where we dump the multiply result |
wire mpyhi; // Return the high half of the multiply |
reg mpyhi; // Return the high half of the multiply |
wire mpybusy; // The multiply is busy if true |
wire mpydone; // True if we'll be valid on the next clock; |
|
// A 4-way multiplexer can be done in one 6-LUT. |
// A 16-way multiplexer can therefore be done in 4x 6-LUT's with |
// the Xilinx multiplexer fabric that follows. |
// the Xilinx multiplexer fabric that follows. |
// Given that we wish to apply this multiplexer approach to 33-bits, |
// this will cost a minimum of 132 6-LUTs. |
|
wire this_is_a_multiply_op; |
assign this_is_a_multiply_op = (i_stb)&&((i_op[3:1]==3'h5)||(i_op[3:0]==4'hc)); |
assign this_is_a_multiply_op = (i_ce)&&((i_op[3:1]==3'h5)||(i_op[3:0]==4'hc)); |
|
// |
// Pull in the multiply logic from elsewhere |
// |
`ifdef FORMAL |
`define MPYOP abs_mpy |
generate |
if (IMPLEMENT_MPY == 0) |
begin // No multiply support. |
assign mpy_result = 63'h00; |
end else if (IMPLEMENT_MPY == 1) |
begin // Our single clock option (no extra clocks) |
wire signed [63:0] w_mpy_a_input, w_mpy_b_input; |
assign w_mpy_a_input = {{(32){(i_a[31])&(i_op[0])}},i_a[31:0]}; |
assign w_mpy_b_input = {{(32){(i_b[31])&(i_op[0])}},i_b[31:0]}; |
assign mpy_result = w_mpy_a_input * w_mpy_b_input; |
assign mpybusy = 1'b0; |
assign mpydone = 1'b0; |
always @(*) mpyhi = 1'b0; // Not needed |
end else if (IMPLEMENT_MPY == 2) |
begin // Our two clock option (ALU must pause for 1 clock) |
reg signed [63:0] r_mpy_a_input, r_mpy_b_input; |
always @(posedge i_clk) |
begin |
r_mpy_a_input <={{(32){(i_a[31])&(i_op[0])}},i_a[31:0]}; |
r_mpy_b_input <={{(32){(i_b[31])&(i_op[0])}},i_b[31:0]}; |
end |
|
assign mpy_result = r_mpy_a_input * r_mpy_b_input; |
assign mpybusy = 1'b0; |
|
reg mpypipe; |
initial mpypipe = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
mpypipe <= 1'b0; |
else |
mpypipe <= (this_is_a_multiply_op); |
|
assign mpydone = mpypipe; // this_is_a_multiply_op; |
always @(posedge i_clk) |
if (this_is_a_multiply_op) |
mpyhi = i_op[1]; |
end else if (IMPLEMENT_MPY == 3) |
begin // Our three clock option (ALU pauses for 2 clocks) |
reg signed [63:0] r_smpy_result; |
reg [63:0] r_umpy_result; |
reg signed [31:0] r_mpy_a_input, r_mpy_b_input; |
reg [1:0] mpypipe; |
reg [1:0] r_sgn; |
|
initial mpypipe = 2'b0; |
always @(posedge i_clk) |
if (i_rst) |
mpypipe <= 2'b0; |
else |
mpypipe <= { mpypipe[0], this_is_a_multiply_op }; |
|
// First clock |
always @(posedge i_clk) |
begin |
r_mpy_a_input <= i_a[31:0]; |
r_mpy_b_input <= i_b[31:0]; |
r_sgn <= { r_sgn[0], i_op[0] }; |
end |
|
// Second clock |
`ifdef VERILATOR |
wire signed [63:0] s_mpy_a_input, s_mpy_b_input; |
wire [63:0] u_mpy_a_input, u_mpy_b_input; |
|
assign s_mpy_a_input = {{(32){r_mpy_a_input[31]}},r_mpy_a_input}; |
assign s_mpy_b_input = {{(32){r_mpy_b_input[31]}},r_mpy_b_input}; |
assign u_mpy_a_input = {32'h00,r_mpy_a_input}; |
assign u_mpy_b_input = {32'h00,r_mpy_b_input}; |
always @(posedge i_clk) |
r_smpy_result = s_mpy_a_input * s_mpy_b_input; |
always @(posedge i_clk) |
r_umpy_result = u_mpy_a_input * u_mpy_b_input; |
`else |
`define MPYOP mpyop |
|
wire [31:0] u_mpy_a_input, u_mpy_b_input; |
|
assign u_mpy_a_input = r_mpy_a_input; |
assign u_mpy_b_input = r_mpy_b_input; |
|
always @(posedge i_clk) |
r_smpy_result = r_mpy_a_input * r_mpy_b_input; |
always @(posedge i_clk) |
r_umpy_result = u_mpy_a_input * u_mpy_b_input; |
`endif |
`MPYOP #(.IMPLEMENT_MPY(IMPLEMENT_MPY)) thempy(i_clk, i_reset, this_is_a_multiply_op, i_op[1:0], |
i_a, i_b, mpydone, mpybusy, mpy_result, mpyhi); |
|
always @(posedge i_clk) |
if (this_is_a_multiply_op) |
mpyhi = i_op[1]; |
assign mpybusy = mpypipe[0]; |
assign mpy_result = (r_sgn[1])?r_smpy_result:r_umpy_result; |
assign mpydone = mpypipe[1]; |
|
// Results are then set on the third clock |
end else // if (IMPLEMENT_MPY <= 4) |
begin // The three clock option |
reg [63:0] r_mpy_result; |
reg [31:0] r_mpy_a_input, r_mpy_b_input; |
reg r_mpy_signed; |
reg [2:0] mpypipe; |
|
// First clock, latch in the inputs |
initial mpypipe = 3'b0; |
always @(posedge i_clk) |
begin |
// mpypipe indicates we have a multiply in the |
// pipeline. In this case, the multiply |
// pipeline is a two stage pipeline, so we need |
// two bits in the pipe. |
if (i_rst) |
mpypipe <= 3'h0; |
else begin |
mpypipe[0] <= this_is_a_multiply_op; |
mpypipe[1] <= mpypipe[0]; |
mpypipe[2] <= mpypipe[1]; |
end |
|
if (i_op[0]) // i.e. if signed multiply |
begin |
r_mpy_a_input <= {(~i_a[31]),i_a[30:0]}; |
r_mpy_b_input <= {(~i_b[31]),i_b[30:0]}; |
end else begin |
r_mpy_a_input <= i_a[31:0]; |
r_mpy_b_input <= i_b[31:0]; |
end |
// The signed bit really only matters in the |
// case of 64 bit multiply. We'll keep track |
// of it, though, and pretend in all other |
// cases. |
r_mpy_signed <= i_op[0]; |
|
if (this_is_a_multiply_op) |
mpyhi = i_op[1]; |
end |
|
assign mpybusy = |mpypipe[1:0]; |
assign mpydone = mpypipe[2]; |
|
// Second clock, do the multiplies, get the "partial |
// products". Here, we break our input up into two |
// halves, |
// |
// A = (2^16 ah + al) |
// B = (2^16 bh + bl) |
// |
// and use these to compute partial products. |
// |
// AB = (2^32 ah*bh + 2^16 (ah*bl + al*bh) + (al*bl) |
// |
// Since we're following the FOIL algorithm to get here, |
// we'll name these partial products according to FOIL. |
// |
// The trick is what happens if A or B is signed. In |
// those cases, the real value of A will not be given by |
// A = (2^16 ah + al) |
// but rather |
// A = (2^16 ah[31^] + al) - 2^31 |
// (where we have flipped the sign bit of A) |
// and so ... |
// |
// AB= (2^16 ah + al - 2^31) * (2^16 bh + bl - 2^31) |
// = 2^32(ah*bh) |
// +2^16 (ah*bl+al*bh) |
// +(al*bl) |
// - 2^31 (2^16 bh+bl + 2^16 ah+al) |
// - 2^62 |
// = 2^32(ah*bh) |
// +2^16 (ah*bl+al*bh) |
// +(al*bl) |
// - 2^31 (2^16 bh+bl + 2^16 ah+al + 2^31) |
// |
reg [31:0] pp_f, pp_l; // F and L from FOIL |
reg [32:0] pp_oi; // The O and I from FOIL |
reg [32:0] pp_s; |
always @(posedge i_clk) |
begin |
pp_f<=r_mpy_a_input[31:16]*r_mpy_b_input[31:16]; |
pp_oi<=r_mpy_a_input[31:16]*r_mpy_b_input[15: 0] |
+ r_mpy_a_input[15: 0]*r_mpy_b_input[31:16]; |
pp_l<=r_mpy_a_input[15: 0]*r_mpy_b_input[15: 0]; |
// And a special one for the sign |
if (r_mpy_signed) |
pp_s <= 32'h8000_0000-( |
r_mpy_a_input[31:0] |
+ r_mpy_b_input[31:0]); |
else |
pp_s <= 33'h0; |
end |
|
// Third clock, add the results and produce a product |
always @(posedge i_clk) |
begin |
r_mpy_result[15:0] <= pp_l[15:0]; |
r_mpy_result[63:16] <= |
{ 32'h00, pp_l[31:16] } |
+ { 15'h00, pp_oi } |
+ { pp_s, 15'h00 } |
+ { pp_f, 16'h00 }; |
end |
|
assign mpy_result = r_mpy_result; |
// Fourth clock -- results are clocked into writeback |
end |
endgenerate // All possible multiply results have been determined |
|
// |
// The master ALU case statement |
// |
always @(posedge i_clk) |
if (i_stb) |
if (i_ce) |
begin |
pre_sign <= (i_a[31]); |
c <= 1'b0; |
155,19 → 333,16
default: o_c <= i_b; // MOV, LDI |
endcase |
end else // if (mpydone) |
// set the output based upon the multiply result |
o_c <= (mpyhi)?mpy_result[63:32]:mpy_result[31:0]; |
|
reg r_busy; |
initial r_busy = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
r_busy <= 1'b0; |
else if (IMPLEMENT_MPY > 1) |
r_busy <= ((i_stb)&&(this_is_a_multiply_op))||mpybusy; |
else |
r_busy <= 1'b0; |
|
if (i_rst) |
r_busy <= 1'b0; |
else |
r_busy <= ((IMPLEMENT_MPY > 1) |
&&(this_is_a_multiply_op))||mpybusy; |
assign o_busy = (r_busy); // ||((IMPLEMENT_MPY>1)&&(this_is_a_multiply_op)); |
|
|
180,108 → 355,11
|
initial o_valid = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
o_valid <= 1'b0; |
else if (IMPLEMENT_MPY <= 1) |
o_valid <= (i_stb); |
else |
o_valid <=((i_stb)&&(!this_is_a_multiply_op))||(mpydone); |
if (i_rst) |
o_valid <= 1'b0; |
else if (IMPLEMENT_MPY <= 1) |
o_valid <= (i_ce); |
else |
o_valid <=((i_ce)&&(!this_is_a_multiply_op))||(mpydone); |
|
`ifdef FORMAL |
initial assume(i_reset); |
reg f_past_valid; |
|
initial f_past_valid = 1'b0; |
always @(posedge i_clk) |
f_past_valid = 1'b1; |
|
`define ASSERT assert |
`ifdef CPUOPS |
`define ASSUME assume |
`else |
`define ASSUME assert |
`endif |
|
// No request should be given us if/while we are busy |
always @(posedge i_clk) |
if (o_busy) |
`ASSUME(!i_stb); |
|
// Following any request other than a multiply request, we should |
// respond in the next cycle |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(o_busy))&&(!$past(this_is_a_multiply_op))) |
`ASSERT(!o_busy); |
|
// Valid and busy can never both be asserted |
always @(posedge i_clk) |
`ASSERT((!o_valid)||(!r_busy)); |
|
// Following any busy, we should always become valid |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_busy))&&(!o_busy)) |
`ASSERT($past(i_reset) || o_valid); |
|
// Check the shift values |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_stb))) |
begin |
if (($past(|i_b[31:6]))||($past(i_b[5:0])>6'd32)) |
begin |
assert(($past(i_op)!=4'h5) |
||({o_c,c}=={(33){1'b0}})); |
assert(($past(i_op)!=4'h6) |
||({c,o_c}=={(33){1'b0}})); |
assert(($past(i_op)!=4'h7) |
||({o_c,c}=={(33){$past(i_a[31])}})); |
end else if ($past(i_b[5:0]==6'd32)) |
begin |
assert(($past(i_op)!=4'h5) |
||(o_c=={(32){1'b0}})); |
assert(($past(i_op)!=4'h6) |
||(o_c=={(32){1'b0}})); |
assert(($past(i_op)!=4'h7) |
||(o_c=={(32){$past(i_a[31])}})); |
end if ($past(i_b)==0) |
begin |
assert(($past(i_op)!=4'h5) |
||({o_c,c}=={$past(i_a), 1'b0})); |
assert(($past(i_op)!=4'h6) |
||({c,o_c}=={1'b0, $past(i_a)})); |
assert(($past(i_op)!=4'h7) |
||({o_c,c}=={$past(i_a), 1'b0})); |
end if ($past(i_b)==1) |
begin |
assert(($past(i_op)!=4'h5) |
||({o_c,c}=={1'b0, $past(i_a)})); |
assert(($past(i_op)!=4'h6) |
||({c,o_c}=={$past(i_a),1'b0})); |
assert(($past(i_op)!=4'h7) |
||({o_c,c}=={$past(i_a[31]),$past(i_a)})); |
end if ($past(i_b)==2) |
begin |
assert(($past(i_op)!=4'h5) |
||({o_c,c}=={2'b0, $past(i_a[31:1])})); |
assert(($past(i_op)!=4'h6) |
||({c,o_c}=={$past(i_a[30:0]),2'b0})); |
assert(($past(i_op)!=4'h7) |
||({o_c,c}=={{(2){$past(i_a[31])}},$past(i_a[31:1])})); |
end if ($past(i_b)==31) |
begin |
assert(($past(i_op)!=4'h5) |
||({o_c,c}=={31'b0, $past(i_a[31:30])})); |
assert(($past(i_op)!=4'h6) |
||({c,o_c}=={$past(i_a[1:0]),31'b0})); |
assert(($past(i_op)!=4'h7) |
||({o_c,c}=={{(31){$past(i_a[31])}},$past(i_a[31:30])})); |
end |
end |
`endif |
endmodule |
// |
// iCE40 NoMPY,w/Shift NoMPY,w/o Shift |
// SB_CARRY 64 64 |
// SB_DFFE 3 3 |
// SB_DFFESR 1 1 |
// SB_DFFSR 33 33 |
// SB_LUT4 748 323 |
/core/dblfetch.v
6,24 → 6,35
// |
// Purpose: This is one step beyond the simplest instruction fetch, |
// prefetch.v. dblfetch.v uses memory pipelining to fetch two |
// (or more) instruction words in one bus cycle. If the CPU consumes |
// either of these before the bus cycle completes, a new request will be |
// made of the bus. In this way, we can keep the CPU filled in spite |
// of a (potentially) slow memory operation. The bus request will end |
// when both requests have been sent and both result locations are empty. |
// instruction words in one cycle, figuring that the unpipelined CPU can't |
// go through both at once, but yet recycles itself fast enough for the |
// next instruction that would follow. It is designed to be a touch |
// faster than the single instruction prefetch, although not as fast as |
// the prefetch and cache found elsewhere. |
// |
// This routine is designed to be a touch faster than the single |
// instruction prefetch (prefetch.v), although not as fast as the |
// prefetch and cache approach found elsewhere (pfcache.v). |
// There are some gotcha's in this logic, however. For example, it's |
// illegal to switch devices mid-transaction, since the second device |
// might have different timing. I.e. the first device might take 8 |
// clocks to create an ACK, and the second device might take 2 clocks, the |
// acks might therefore come on top of each other, or even out of order. |
// But ... in order to keep logic down, we keep track of the PC in the |
// o_wb_addr register. Hence, this register gets changed on any i_new_pc. |
// The i_pc value associated with i_new_pc will only be valid for one |
// clock, hence we can't wait to change. To keep from violating the WB |
// rule, therefore, we *must* immediately stop requesting any transaction, |
// and then terminate the bus request as soon as possible. |
// |
// 20180222: Completely rebuilt. |
// This has consequences in terms of logic used, leaving this routine |
// anything but simple--even though the number of wires affected by |
// this is small (o_wb_cyc, o_wb_stb, and last_ack). |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2017-2019, Gisselquist Technology, LLC |
// Copyright (C) 2017, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
47,29 → 58,27
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
// |
module dblfetch(i_clk, i_reset, i_new_pc, i_clear_cache, |
i_stall_n, i_pc, o_insn, o_pc, o_valid, |
module dblfetch(i_clk, i_rst, i_new_pc, i_clear_cache, |
i_stall_n, i_pc, o_i, o_pc, o_v, |
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, |
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data, |
o_illegal); |
parameter ADDRESS_WIDTH=30, AUX_WIDTH = 1; |
localparam AW=ADDRESS_WIDTH, DW = 32; |
input wire i_clk, i_reset, i_new_pc, i_clear_cache, |
parameter ADDRESS_WIDTH=32, AUX_WIDTH = 1; |
localparam AW=ADDRESS_WIDTH; |
input i_clk, i_rst, i_new_pc, i_clear_cache, |
i_stall_n; |
input wire [(AW+1):0] i_pc; |
output reg [(DW-1):0] o_insn; |
output reg [(AW+1):0] o_pc; |
output reg o_valid; |
input [(AW-1):0] i_pc; |
output reg [31:0] o_i; |
output reg [(AW-1):0] o_pc; |
output wire o_v; |
// Wishbone outputs |
output reg o_wb_cyc, o_wb_stb; |
output wire o_wb_we; |
output reg [(AW-1):0] o_wb_addr; |
output wire [(DW-1):0] o_wb_data; |
output wire [31:0] o_wb_data; |
// And return inputs |
input wire i_wb_ack, i_wb_stall, i_wb_err; |
input wire [(DW-1):0] i_wb_data; |
input i_wb_ack, i_wb_stall, i_wb_err; |
input [31:0] i_wb_data; |
// And ... the result if we got an error |
output reg o_illegal; |
|
76,67 → 85,87
assign o_wb_we = 1'b0; |
assign o_wb_data = 32'h0000; |
|
reg last_stb, invalid_bus_cycle; |
reg last_ack, last_stb, invalid_bus_cycle; |
|
reg [(DW-1):0] cache_word; |
reg cache_valid; |
reg [1:0] inflight; |
reg cache_illegal; |
reg [31:0] cache [0:1]; |
reg cache_read_addr, cache_write_addr; |
reg [1:0] cache_valid; |
|
initial o_wb_cyc = 1'b0; |
initial o_wb_stb = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||((o_wb_cyc)&&(i_wb_err))) |
if ((i_rst)||(i_wb_err)) |
begin |
o_wb_cyc <= 1'b0; |
o_wb_stb <= 1'b0; |
// last_stb <= 1'b0; |
// last_ack <= 1'b0; |
end else if (o_wb_cyc) |
begin |
if ((!o_wb_stb)||(!i_wb_stall)) |
o_wb_stb <= (!last_stb); |
if ((o_wb_stb)&&(!i_wb_stall)) |
begin |
// last_stb <= 1'b1; |
o_wb_stb <= !last_stb; |
end |
// if (i_wb_ack) |
// last_ack <= 1'b1; |
if ((i_new_pc)||(invalid_bus_cycle)) |
o_wb_stb <= 1'b0; |
|
// Relase the bus on the second ack |
if (((i_wb_ack)&&(!o_wb_stb)&&(inflight<=1)) |
||((!o_wb_stb)&&(inflight == 0)) |
// Or any new transaction request |
||((i_new_pc)||(i_clear_cache))) |
if ((i_wb_ack)&&( |
// Relase the bus on the second ack |
(last_ack) |
// Or on the first ACK, if we've been told |
// we have an invalid bus cycle |
||((o_wb_stb)&&(i_wb_stall)&&(last_stb)&&( |
(i_new_pc)||(invalid_bus_cycle))) |
)) |
begin |
o_wb_cyc <= 1'b0; |
o_wb_stb <= 1'b0; |
end |
|
end else if ((i_new_pc)||(invalid_bus_cycle) |
||((o_valid)&&(i_stall_n)&&(!o_illegal))) |
if ((!last_stb)&&(i_wb_stall)&&((i_new_pc)||(invalid_bus_cycle))) |
// Also release the bus with no acks, if we |
// haven't made any requests |
begin |
o_wb_cyc <= 1'b0; |
o_wb_stb <= 1'b0; |
end |
end else if ((invalid_bus_cycle) |
||((o_v)&&(i_stall_n)&&(cache_read_addr))) // Initiate a bus cycle |
begin |
// Initiate a bus cycle if ... the last bus cycle was |
// aborted (bus error or new_pc), we've been given a |
// new PC to go get, or we just exhausted our one |
// instruction cache |
o_wb_cyc <= 1'b1; |
o_wb_stb <= 1'b1; |
// last_stb <= 1'b0; |
// last_ack <= 1'b0; |
end |
|
initial inflight = 2'b00; |
initial last_stb = 1'b0; |
always @(posedge i_clk) |
if (!o_wb_cyc) |
inflight <= 2'b00; |
else begin |
case({ ((o_wb_stb)&&(!i_wb_stall)), i_wb_ack }) |
2'b01: inflight <= inflight - 1'b1; |
2'b10: inflight <= inflight + 1'b1; |
// If neither ack nor request, then no change. Likewise |
// if we have both an ack and a request, there's no change |
// in the number of requests in flight. |
default: begin end |
endcase |
end |
if ((o_wb_cyc)&&(o_wb_stb)&&(!i_wb_stall)) |
last_stb <= 1'b1; |
else if (!o_wb_cyc) |
last_stb <= 1'b0; |
|
always @(*) |
last_stb = (inflight != 2'b00)||((o_valid)&&(!i_stall_n)); |
initial last_ack = 1'b0; |
always @(posedge i_clk) |
if ((o_wb_cyc)&&(i_wb_ack)) |
last_ack <= 1'b1; |
else if ((o_wb_cyc)&&(o_wb_stb)&&(i_wb_stall)&&( |
(i_new_pc)||(invalid_bus_cycle))) |
last_ack <= 1'b1; |
else if ((o_wb_cyc)&&(o_wb_stb)&&(!i_wb_stall)&&(!last_stb)&&( |
(i_new_pc)||(invalid_bus_cycle))) |
last_ack <= 1'b1; |
else if (!o_wb_cyc) |
last_ack <= 1'b0; |
|
initial invalid_bus_cycle = 1'b0; |
always @(posedge i_clk) |
if ((o_wb_cyc)&&(i_new_pc)) |
if (i_rst) |
invalid_bus_cycle <= 1'b0; |
else if ((i_new_pc)||(i_clear_cache)) |
invalid_bus_cycle <= 1'b1; |
else if (!o_wb_cyc) |
invalid_bus_cycle <= 1'b0; |
144,573 → 173,60
initial o_wb_addr = {(AW){1'b1}}; |
always @(posedge i_clk) |
if (i_new_pc) |
o_wb_addr <= i_pc[AW+1:2]; |
else if ((o_wb_stb)&&(!i_wb_stall)) |
o_wb_addr <= i_pc; |
else if ((o_wb_stb)&&(!i_wb_stall)&&(!invalid_bus_cycle)) |
o_wb_addr <= o_wb_addr + 1'b1; |
|
////////////////// |
// |
// Now for the immediate output word to the CPU |
// |
////////////////// |
|
initial o_valid = 1'b0; |
initial cache_write_addr = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(i_new_pc)||(i_clear_cache)) |
o_valid <= 1'b0; |
else if ((o_wb_cyc)&&((i_wb_ack)||(i_wb_err))) |
o_valid <= 1'b1; |
else if (i_stall_n) |
o_valid <= cache_valid; |
if (!o_wb_cyc) |
cache_write_addr <= 1'b0; |
else if ((o_wb_cyc)&&(i_wb_ack)) |
cache_write_addr <= cache_write_addr + 1'b1; |
|
always @(posedge i_clk) |
if ((!o_valid)||(i_stall_n)) |
begin |
if (cache_valid) |
o_insn <= cache_word; |
else |
o_insn <= i_wb_data; |
end |
if ((o_wb_cyc)&&(i_wb_ack)) |
cache[cache_write_addr] <= i_wb_data; |
|
initial o_pc[1:0] = 2'b00; |
initial cache_read_addr = 1'b0; |
always @(posedge i_clk) |
if (i_new_pc) |
o_pc <= i_pc; |
else if ((o_valid)&&(i_stall_n)) |
o_pc[AW+1:2] <= o_pc[AW+1:2] + 1'b1; |
if ((i_new_pc)||(invalid_bus_cycle) |
||((o_v)&&(cache_read_addr)&&(i_stall_n))) |
cache_read_addr <= 1'b0; |
else if ((o_v)&&(i_stall_n)) |
cache_read_addr <= 1'b1; |
|
initial o_illegal = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(i_new_pc)||(i_clear_cache)) |
o_illegal <= 1'b0; |
else if ((!o_valid)||(i_stall_n)) |
begin |
if (cache_valid) |
o_illegal <= (o_illegal)||(cache_illegal); |
else if ((o_wb_cyc)&&(i_wb_err)) |
o_illegal <= 1'b1; |
end |
|
|
////////////////// |
// |
// Now for the output/cached word |
// |
////////////////// |
|
initial cache_valid = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(i_new_pc)||(i_clear_cache)) |
cache_valid <= 1'b0; |
if ((i_new_pc)||(invalid_bus_cycle)) |
cache_valid <= 2'b00; |
else begin |
if ((o_valid)&&(o_wb_cyc)&&((i_wb_ack)||(i_wb_err))) |
cache_valid <= (!i_stall_n)||(cache_valid); |
else if (i_stall_n) |
cache_valid <= 1'b0; |
if ((o_v)&&(i_stall_n)) |
cache_valid[cache_read_addr] <= 1'b0; |
if ((o_wb_cyc)&&(i_wb_ack)) |
cache_valid[cache_write_addr] <= 1'b1; |
end |
|
initial o_i = {(32){1'b1}}; |
always @(posedge i_clk) |
if ((o_wb_cyc)&&(i_wb_ack)) |
cache_word <= i_wb_data; |
|
initial cache_illegal = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(i_clear_cache)||(i_new_pc)) |
cache_illegal <= 1'b0; |
else if ((o_wb_cyc)&&(i_wb_err)&&(o_valid)&&(!i_stall_n)) |
cache_illegal <= 1'b1; |
// |
// Some of these properties can be done in yosys-smtbmc, *or* Verilator |
// |
// Ver1lator is different from yosys, however, in that Verilator doesn't support |
// the $past() directive. Further, any `assume`'s turn into `assert()`s |
// within Verilator. We can use this to help prove that the properties |
// of interest truly hold, and that any contracts we create or assumptions we |
// make truly hold in practice (i.e. in simulation). |
// |
`ifdef FORMAL |
`define VERILATOR_FORMAL |
`else |
`ifdef VERILATOR |
// |
// Define VERILATOR_FORMAL here to have Verilator check your formal properties |
// during simulation. assert() and assume() statements will both have the |
// same effect within VERILATOR of causing your simulation to suddenly end. |
// |
// I have this property commented because it only works on the newest versions |
// of Verilator (3.9 something and later), and I tend to still use Verilator |
// 3.874. |
// |
// `define VERILATOR_FORMAL |
`endif |
`endif |
|
`ifdef VERILATOR_FORMAL |
// Keep track of a flag telling us whether or not $past() |
// will return valid results |
reg f_past_valid; |
initial f_past_valid = 1'b0; |
always @(posedge i_clk) |
f_past_valid = 1'b1; |
|
// Keep track of some alternatives to $past that can still be used |
// in a VERILATOR environment |
reg f_past_reset, f_past_clear_cache, f_past_o_valid, |
f_past_stall_n; |
|
initial f_past_reset = 1'b1; |
initial f_past_clear_cache = 1'b0; |
initial f_past_o_valid = 1'b0; |
initial f_past_stall_n = 1'b1; |
always @(posedge i_clk) |
begin |
f_past_reset <= i_reset; |
f_past_clear_cache <= i_clear_cache; |
f_past_o_valid <= o_valid; |
f_past_stall_n <= i_stall_n; |
end |
`endif |
|
`ifdef FORMAL |
// |
// |
// Generic setup |
// |
// |
`ifdef DBLFETCH |
`define ASSUME assume |
`else |
`define ASSUME assert |
`endif |
|
///////////////////////////////////////////////// |
// |
// |
// Assumptions about our inputs |
// |
// |
///////////////////////////////////////////////// |
|
always @(*) |
if (!f_past_valid) |
`ASSUME(i_reset); |
|
// |
// Assume that resets, new-pc commands, and clear-cache commands |
// are never more than pulses--one clock wide at most. |
// |
// It may be that the CPU treats us differently. We'll only restrict |
// our solver to this here. |
/* |
always @(posedge i_clk) |
if (f_past_valid) |
begin |
if (f_past_reset) |
restrict(!i_reset); |
if ($past(i_new_pc)) |
restrict(!i_new_pc); |
end |
*/ |
|
// |
// Assume we start from a reset condition |
initial assume(i_reset); |
|
///////////////////////////////////////////////// |
// |
// |
// Wishbone bus properties |
// |
// |
///////////////////////////////////////////////// |
|
localparam F_LGDEPTH=2; |
wire [(F_LGDEPTH-1):0] f_nreqs, f_nacks, f_outstanding; |
|
// |
// Add a bunch of wishbone-based asserts |
fwb_master #(.AW(AW), .DW(DW), .F_LGDEPTH(F_LGDEPTH), |
.F_MAX_STALL(2), |
.F_MAX_REQUESTS(0), .F_OPT_SOURCE(1), |
.F_OPT_RMW_BUS_OPTION(1), |
.F_OPT_DISCONTINUOUS(0)) |
f_wbm(i_clk, i_reset, |
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, 4'h0, |
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err, |
f_nreqs, f_nacks, f_outstanding); |
|
`endif |
|
// |
// Now, apply the following to Verilator *or* yosys-smtbmc |
// |
`ifdef VERILATOR_FORMAL |
///////////////////////////////////////////////// |
// |
// |
// Assumptions about our interaction with the CPU |
// |
// |
///////////////////////////////////////////////// |
|
// Assume that any reset is either accompanied by a new address, |
// or a new address immediately follows it. |
always @(posedge i_clk) |
if ((f_past_valid)&&(f_past_reset)) |
assume(i_new_pc); |
|
always @(posedge i_clk) |
if (f_past_clear_cache) |
assume(!i_clear_cache); |
|
// |
// |
// The bottom two bits of the PC address register are always zero. |
// They are there to make examining traces easier, but I expect |
// the synthesis tool to remove them. |
// |
always @(*) |
assume(i_pc[1:0] == 2'b00); |
|
// Some things to know from the CPU ... there will always be a |
// i_new_pc request following any reset |
always @(posedge i_clk) |
if ((f_past_valid)&&(f_past_reset)) |
assume(i_new_pc); |
|
// There will also be a i_new_pc request following any request to clear |
// the cache. |
always @(posedge i_clk) |
if ((f_past_valid)&&(f_past_clear_cache)) |
assume(i_new_pc); |
|
always @(posedge i_clk) |
if (f_past_clear_cache) |
assume(!i_clear_cache); |
|
always @(*) |
assume(i_pc[1:0] == 2'b00); |
`endif |
|
`ifdef FORMAL |
// |
// Let's make some assumptions about how long it takes our phantom |
// (i.e. assumed) CPU to respond. |
// |
// This delay needs to be long enough to flush out any potential |
// errors, yet still short enough that the formal method doesn't |
// take forever to solve. |
// |
`ifdef DBLFETCH |
localparam F_CPU_DELAY = 4; |
reg [4:0] f_cpu_delay; |
|
// Now, let's look at the delay the CPU takes to accept an instruction. |
always @(posedge i_clk) |
// If no instruction is ready, then keep our counter at zero |
if ((!o_valid)||(i_stall_n)) |
f_cpu_delay <= 0; |
if ((i_stall_n)&&(o_wb_cyc)&&(i_wb_ack)) |
o_i <= i_wb_data; |
else |
// Otherwise, count the clocks the CPU takes to respond |
f_cpu_delay <= f_cpu_delay + 1'b1; |
o_i <= cache[cache_read_addr]; |
|
initial o_pc = 0; |
always @(posedge i_clk) |
assume(f_cpu_delay < F_CPU_DELAY); |
`endif |
if (i_new_pc) |
o_pc <= i_pc; |
else if ((o_v)&&(i_stall_n)) |
o_pc <= o_pc + 1'b1; |
|
assign o_v = cache_valid[cache_read_addr]; |
|
|
///////////////////////////////////////////////// |
// |
// |
// Assertions about our outputs |
// |
// |
///////////////////////////////////////////////// |
initial o_illegal = 1'b0; |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_wb_stb))&&(!$past(i_wb_stall)) |
&&(!$past(i_new_pc))) |
assert(o_wb_addr <= $past(o_wb_addr)+1'b1); |
if ((o_wb_cyc)&&(i_wb_err)) |
o_illegal <= 1'b1; |
else if ((!o_wb_cyc)&&((i_new_pc)||(invalid_bus_cycle))) |
o_illegal <= 1'b0; |
|
// |
// Assertions about our return responses to the CPU |
// |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset)) |
&&(!$past(i_new_pc))&&(!$past(i_clear_cache)) |
&&($past(o_valid))&&(!$past(i_stall_n))) |
begin |
assert($stable(o_pc)); |
assert($stable(o_insn)); |
assert($stable(o_valid)); |
assert($stable(o_illegal)); |
end |
|
// The same is true of the cache as well. |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset)) |
&&(!$past(i_new_pc))&&(!$past(i_clear_cache)) |
&&($past(o_valid))&&(!$past(i_stall_n)) |
&&($past(cache_valid))) |
begin |
assert($stable(cache_valid)); |
assert($stable(cache_word)); |
assert($stable(cache_illegal)); |
end |
|
// Consider it invalid to present the CPU with the same instruction |
// twice in a row. Any effort to present the CPU with the same |
// instruction twice in a row must go through i_new_pc, and thus a |
// new bus cycle--hence the assertion below makes sense. |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_new_pc)) |
&&($past(o_valid))&&($past(i_stall_n))) |
assert(o_pc[AW+1:2] == $past(o_pc[AW+1:2])+1'b1); |
|
|
// |
// As with i_pc[1:0], the bottom two bits of the address are unused. |
// Let's assert here that they remain zero. |
always @(*) |
assert(o_pc[1:0] == 2'b00); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset)) |
&&(!$past(i_new_pc)) |
&&(!$past(i_clear_cache)) |
&&($past(o_wb_cyc))&&($past(i_wb_err))) |
assert( ((o_valid)&&(o_illegal)) |
||((cache_valid)&&(cache_illegal)) ); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(o_illegal))&&(o_illegal)) |
assert(o_valid); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(cache_illegal))&&(!cache_valid)) |
assert(!cache_illegal); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_new_pc))) |
assert(!o_valid); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&(!$past(i_clear_cache)) |
&&($past(o_valid))&&(!o_valid)&&(!o_illegal)) |
assert((o_wb_cyc)||(invalid_bus_cycle)); |
|
///////////////////////////////////////////////// |
// |
// |
// Our "contract" with the CPU |
// |
// |
///////////////////////////////////////////////// |
// |
// For any particular address, that address is associated with an |
// instruction and a flag regarding whether or not it is illegal. |
// |
// Any attempt to return to the CPU a value from this address, |
// must return the value and the illegal flag. |
// |
(* anyconst *) reg [AW-1:0] f_const_addr; |
(* anyconst *) reg [DW-1:0] f_const_insn; |
(* anyconst *) reg f_const_illegal; |
|
// |
// While these wires may seem like overkill, and while they make the |
// following logic perhaps a bit more obscure, these predicates make |
// it easier to follow the complex logic on a scope. They don't |
// affect anything synthesized. |
// |
wire f_this_addr, f_this_pc, f_this_req, f_this_data, |
f_this_insn; |
|
assign f_this_addr = (o_wb_addr == f_const_addr); |
assign f_this_pc = (o_pc == { f_const_addr, 2'b00 }); |
assign f_this_req = (i_pc == { f_const_addr, 2'b00 }); |
assign f_this_data = (i_wb_data == f_const_insn); |
assign f_this_insn = (o_insn == f_const_insn); |
|
|
// |
// |
// Here's our contract: |
// |
// Any time we return a value for the address above, it *must* be |
// the "right" value. |
// |
always @(*) |
if ((o_valid)&&(f_this_pc)) |
begin |
if (f_const_illegal) |
assert(o_illegal); |
if (!o_illegal) |
assert(f_this_insn); |
end |
|
// |
// The contract will only work if we assume the return from the |
// bus at this address will be the right return. |
wire f_this_return; |
assign f_this_return = (o_wb_addr - f_outstanding == f_const_addr); |
always @(*) |
if ((o_wb_cyc)&&(f_this_return)) |
begin |
if (i_wb_ack) |
assume(i_wb_data == f_const_insn); |
|
if (f_const_illegal) |
assume(!i_wb_ack); |
else |
assume(!i_wb_err); |
end |
|
// |
// Here is a corrollary to our contract. Anything in the one-word |
// cache must also match the contract as well. |
// |
always @(*) |
if ((o_pc[AW+1:2] + 1'b1 == f_const_addr)&&(cache_valid)) |
begin |
if (!cache_illegal) |
assert(cache_word == f_const_insn); |
|
if (f_const_illegal) |
assert(cache_illegal); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(cache_illegal))&&(!cache_valid)) |
assert(!cache_illegal); |
|
//////////////////////////////////////////////////////// |
// |
// |
// Additional assertions necessary to pass induction |
// |
// |
//////////////////////////////////////////////////////// |
// |
// We have only a one word cache. Hence, we shouldn't be asking |
// for more data any time we have nowhere to put it. |
always @(*) |
if (o_wb_stb) |
assert((!cache_valid)||(i_stall_n)); |
|
always @(*) |
if ((o_valid)&&(cache_valid)) |
assert((f_outstanding == 0)&&(!o_wb_stb)); |
|
always @(*) |
if ((o_valid)&&(!i_stall_n)) |
assert(f_outstanding < 2); |
|
always @(*) |
if ((!o_valid)||(i_stall_n)) |
assert(f_outstanding <= 2); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_wb_cyc))&&(!$past(o_wb_stb)) |
&&(o_wb_cyc)) |
assert(inflight != 0); |
|
always @(*) |
if ((o_wb_cyc)&&(i_wb_ack)) |
assert(!cache_valid); |
|
always @(posedge i_clk) |
if (o_wb_cyc) |
assert(inflight == f_outstanding); |
|
wire [AW-1:0] this_return_address, |
next_pc_address; |
assign this_return_address = o_wb_addr - f_outstanding; |
assign next_pc_address = o_pc[AW+1:2] + 1'b1; |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_wb_cyc)) |
&&(!$past(i_reset)) |
&&(!$past(i_new_pc)) |
&&(!$past(i_clear_cache)) |
&&(!$past(invalid_bus_cycle)) |
&&(($past(i_wb_ack))||($past(i_wb_err))) |
&&((!$past(o_valid))||($past(i_stall_n))) |
&&(!$past(cache_valid))) |
assert(o_pc[AW+1:2] == $past(this_return_address)); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_wb_cyc))&&(!o_valid)&&(!$past(i_new_pc)) |
&&(o_wb_cyc)) |
assert(o_pc[AW+1:2] == this_return_address); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_wb_cyc)) |
&&(!$past(cache_valid))&&(cache_valid)) |
assert(next_pc_address == $past(this_return_address)); |
|
|
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_wb_cyc))&&(o_wb_cyc)) |
begin |
if ((o_valid)&&(!cache_valid)) |
assert(this_return_address == next_pc_address); |
else if (!o_valid) |
assert(this_return_address == o_pc[AW+1:2]); |
end else if ((f_past_valid)&&(!invalid_bus_cycle) |
&&(!o_wb_cyc)&&(o_valid)&&(!o_illegal) |
&&(!cache_valid)) |
assert(o_wb_addr == next_pc_address); |
|
|
always @(*) |
if (invalid_bus_cycle) |
assert(!o_wb_cyc); |
always @(*) |
if (cache_valid) |
assert(o_valid); |
|
///////////////////////////////////////////////////// |
// |
// |
// Cover statements |
// |
// |
///////////////////////////////////////////////////// |
|
always @(posedge i_clk) |
cover((f_past_valid)&&($past(f_nacks)==3) |
&&($past(i_wb_ack))&&($past(o_wb_cyc))); |
|
|
///////////////////////////////////////////////////// |
// |
// |
// Temporary simplifications |
// |
// |
///////////////////////////////////////////////////// |
|
// always @(*) |
// assume((!i_wb_err)&&(!f_const_illegal)); |
|
|
`endif // FORMAL |
endmodule |
// |
// Usage: (this) (prior) (old) (S6) |
// Cells 374 387 585 459 |
// FDRE 135 108 203 171 |
// LUT1 2 3 2 |
// LUT2 9 3 4 5 |
// LUT3 98 76 104 71 |
// LUT4 2 0 2 2 |
// LUT5 3 35 35 3 |
// LUT6 6 5 10 43 |
// MUXCY 58 62 93 62 |
// MUXF7 1 0 2 3 |
// MUXF8 0 1 1 |
// RAM64X1D 0 32 32 32 |
// XORCY 60 64 96 64 |
// |
/core/div.v
8,10 → 8,10
// for both signed and unsigned divide. |
// |
// Steps: |
// i_reset The DIVide unit starts in idle. It can also be placed into an |
// i_rst The DIVide unit starts in idle. It can also be placed into an |
// idle by asserting the reset input. |
// |
// i_wr When i_reset is asserted, a divide begins. On the next clock: |
// i_wr When i_rst is asserted, a divide begins. On the next clock: |
// |
// o_busy is set high so everyone else knows we are at work and they can |
// wait for us to complete. |
71,7 → 71,7
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2019, Gisselquist Technology, LLC |
// Copyright (C) 2015-2017, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
95,17 → 95,15
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
// |
// `include "cpudefs.v" |
// |
module div(i_clk, i_reset, i_wr, i_signed, i_numerator, i_denominator, |
module div(i_clk, i_rst, i_wr, i_signed, i_numerator, i_denominator, |
o_busy, o_valid, o_err, o_quotient, o_flags); |
parameter BW=32, LGBW = 5; |
input wire i_clk, i_reset; |
input i_clk, i_rst; |
// Input parameters |
input wire i_wr, i_signed; |
input wire [(BW-1):0] i_numerator, i_denominator; |
input i_wr, i_signed; |
input [(BW-1):0] i_numerator, i_denominator; |
// Output parameters |
output reg o_busy, o_valid, o_err; |
output reg [(BW-1):0] o_quotient; |
115,10 → 113,11
// before we are valid, so it can't be o_busy ... |
// |
reg r_busy; |
reg [BW-1:0] r_divisor; |
reg [(2*BW-2):0] r_dividend; |
reg [(2*BW-2):0] r_divisor; |
reg [(BW-1):0] r_dividend; |
wire [(BW):0] diff; // , xdiff[(BW-1):0]; |
assign diff = r_dividend[2*BW-2:BW-1] - r_divisor; |
assign diff = r_dividend - r_divisor[(BW-1):0]; |
// assign xdiff= r_dividend - { 1'b0, r_divisor[(BW-1):1] }; |
|
reg r_sign, pre_sign, r_z, r_c, last_bit; |
reg [(LGBW-1):0] r_bit; |
131,12 → 130,12
// or equivalently when we discover we are dividing by zero. |
initial r_busy = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
r_busy <= 1'b0; |
else if (i_wr) |
r_busy <= 1'b1; |
else if ((last_bit)||(zero_divisor)) |
r_busy <= 1'b0; |
if (i_rst) |
r_busy <= 1'b0; |
else if (i_wr) |
r_busy <= 1'b1; |
else if ((last_bit)||(zero_divisor)) |
r_busy <= 1'b0; |
|
// o_busy is very similar to r_busy, save for some key differences. |
// Primary among them is that o_busy needs to (possibly) be true |
146,18 → 145,32
// identical. |
initial o_busy = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
o_busy <= 1'b0; |
else if (i_wr) |
o_busy <= 1'b1; |
else if (((last_bit)&&(!r_sign))||(zero_divisor)) |
o_busy <= 1'b0; |
else if (!r_busy) |
o_busy <= 1'b0; |
if (i_rst) |
o_busy <= 1'b0; |
else if (i_wr) |
o_busy <= 1'b1; |
else if (((last_bit)&&(~r_sign))||(zero_divisor)) |
o_busy <= 1'b0; |
else if (~r_busy) |
o_busy <= 1'b0; |
|
// If we are asked to divide by zero, we need to halt. The sooner |
// we halt and report the error, the better. Hence, here we look |
// for a zero divisor while being busy. The always above us will then |
// look at this and halt a divide in the middle if we are trying to |
// divide by zero. |
// |
// Note that this works off of the 2BW-1 length vector. If we can |
// simplify that, it should simplify our logic as well. |
initial zero_divisor = 1'b0; |
always @(posedge i_clk) |
if (i_wr) |
zero_divisor <= (i_denominator == 0); |
// zero_divisor <= (r_divisor == 0)&&(r_busy); |
if (i_rst) |
zero_divisor <= 1'b0; |
else if (i_wr) |
zero_divisor <= (i_denominator == 0); |
else if (!r_busy) |
zero_divisor <= 1'b0; |
|
// o_valid is part of the ZipCPU protocol. It will be set to true |
// anytime our answer is valid and may be used by the calling module. |
170,19 → 183,17
// it on an i_wr signal. |
initial o_valid = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(o_valid)) |
o_valid <= 1'b0; |
else if ((r_busy)&&(zero_divisor)) |
o_valid <= 1'b1; |
else if (r_busy) |
begin |
if (last_bit) |
o_valid <= (!r_sign); |
end else if (r_sign) |
begin |
o_valid <= 1'b1; |
end else |
o_valid <= 1'b0; |
if (i_rst) |
o_valid <= 1'b0; |
else if (r_busy) |
begin |
if ((last_bit)||(zero_divisor)) |
o_valid <= (zero_divisor)||(!r_sign); |
end else if (r_sign) |
begin |
o_valid <= (!zero_divisor); // 1'b1; |
end else |
o_valid <= 1'b0; |
|
// Division by zero error reporting. Anytime we detect a zero divisor, |
// we set our output error, and then hold it until we are valid and |
189,12 → 200,12
// everything clears. |
initial o_err = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
o_err <= 1'b0; |
else if ((r_busy)&&(zero_divisor)) |
o_err <= 1'b1; |
else |
o_err <= 1'b0; |
if((i_rst)||(o_valid)) |
o_err <= 1'b0; |
else if (((r_busy)||(r_sign))&&(zero_divisor)) |
o_err <= 1'b1; |
else |
o_err <= 1'b0; |
|
// r_bit |
// |
202,31 → 213,26
// ranges from 31 down to zero. On any write, we set ourselves to |
// 5'h1f. Otherwise, while we are busy (but not within the pre-sign |
// adjustment stage), we subtract one from our value on every clock. |
initial r_bit = 0; |
always @(posedge i_clk) |
if (i_reset) |
r_bit <= 0; |
else if ((r_busy)&&(!pre_sign)) |
r_bit <= r_bit + 1'b1; |
else |
r_bit <= 0; |
if ((r_busy)&&(!pre_sign)) |
r_bit <= r_bit + {(LGBW){1'b1}}; |
else |
r_bit <= {(LGBW){1'b1}}; |
|
// last_bit |
// |
// This logic replaces a lot of logic that was inside our giant state |
// machine with ... something simpler. In particular, we'll use this |
// logic to determine if we are processing our last bit. The only trick |
// is, this bit needs to be set whenever (r_busy) and (r_bit == -1), |
// hence we need to set on (r_busy) and (r_bit == -2) so as to be set |
// logic to determine we are processing our last bit. The only trick |
// is, this bit needs to be set whenever (r_busy) and (r_bit == 0), |
// hence we need to set on (r_busy) and (r_bit == 1) so as to be set |
// when (r_bit == 0). |
initial last_bit = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
last_bit <= 1'b0; |
else if (r_busy) |
last_bit <= (r_bit == {(LGBW){1'b1}}-1'b1); |
else |
last_bit <= 1'b0; |
if (r_busy) |
last_bit <= (r_bit == {{(LGBW-1){1'b0}},1'b1}); |
else |
last_bit <= 1'b0; |
|
// pre_sign |
// |
235,59 → 241,67
// be true for the one clock, and then it must clear itself. |
initial pre_sign = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
pre_sign <= 1'b0; |
else |
pre_sign <= (i_wr)&&(i_signed)&&((i_numerator[BW-1])||(i_denominator[BW-1])); |
if (i_wr) |
pre_sign <= i_signed; |
else |
pre_sign <= 1'b0; |
|
// As a result of our operation, we need to set the flags. The most |
// difficult of these is the "Z" flag indicating that the result is |
// zero. Here, we'll use the same logic that sets the low-order |
// bit to clear our zero flag, and leave the zero flag set in all |
// other cases. |
// other cases. Well ... not quite. If we need to flip the sign of |
// our value, then we can't quite clear the zero flag ... yet. |
always @(posedge i_clk) |
if (i_wr) |
r_z <= 1'b1; |
else if ((r_busy)&&(!pre_sign)&&(!diff[BW])) |
r_z <= 1'b0; |
if((r_busy)&&(r_divisor[(2*BW-2):(BW)] == 0)&&(!diff[BW])) |
// If we are busy, the upper bits of our divisor are |
// zero (i.e., we got the shift right), and the top |
// (carry) bit of the difference is zero (no overflow), |
// then we could subtract our divisor from our dividend |
// and hence we add a '1' to the quotient, while setting |
// the zero flag to false. |
r_z <= 1'b0; |
else if ((!r_busy)&&(!r_sign)) |
r_z <= 1'b1; |
|
// r_dividend |
// This is initially the numerator. On a signed divide, it then becomes |
// the absolute value of the numerator. We'll subtract from this value |
// the divisor for every output bit we are looking for--just as with |
// traditional long division. |
// the divisor shifted as appropriate for every output bit we are |
// looking for--just as with traditional long division. |
always @(posedge i_clk) |
if (pre_sign) |
begin |
// If we are doing a signed divide, then take the |
// absolute value of the dividend |
if (r_dividend[BW-1]) |
if (pre_sign) |
begin |
r_dividend[2*BW-2:0] <= {(2*BW-1){1'b1}}; |
r_dividend[BW-1:0] <= -r_dividend[BW-1:0]; |
end |
end else if (r_busy) |
begin |
r_dividend <= { r_dividend[2*BW-3:0], 1'b0 }; |
if (!diff[BW]) |
r_dividend[2*BW-2:BW] <= diff[(BW-2):0]; |
end else if (!r_busy) |
// Once we are done, and r_busy is no longer high, we'll |
// always accept new values into our dividend. This |
// guarantees that, when i_wr is set, the new value |
// is already set as desired. |
r_dividend <= { 31'h0, i_numerator }; |
// If we are doing a signed divide, then take the |
// absolute value of the dividend |
if (r_dividend[BW-1]) |
r_dividend <= -r_dividend; |
// The begin/end block is important so we don't lose |
// the fact that on an else we don't do anything. |
end else if((r_busy)&&(r_divisor[(2*BW-2):(BW)]==0)&&(!diff[BW])) |
// This is the condition whereby we set a '1' in our |
// output quotient, and we subtract the (current) |
// divisor from our dividend. (The difference is |
// already kept in the diff vector above.) |
r_dividend <= diff[(BW-1):0]; |
else if (!r_busy) |
// Once we are done, and r_busy is no longer high, we'll |
// always accept new values into our dividend. This |
// guarantees that, when i_wr is set, the new value |
// is already set as desired. |
r_dividend <= i_numerator; |
|
initial r_divisor = 0; |
always @(posedge i_clk) |
if (i_reset) |
r_divisor <= 0; |
else if ((pre_sign)&&(r_busy)) |
begin |
if (r_divisor[BW-1]) |
r_divisor <= -r_divisor; |
end else if (!r_busy) |
r_divisor <= i_denominator; |
if (pre_sign) |
begin |
if (r_divisor[(2*BW-2)]) |
r_divisor[(2*BW-2):(BW-1)] |
<= -r_divisor[(2*BW-2):(BW-1)]; |
end else if (r_busy) |
r_divisor <= { 1'b0, r_divisor[(2*BW-2):1] }; |
else |
r_divisor <= { i_denominator, {(BW-1){1'b0}} }; |
|
// r_sign |
// is a flag for our state machine control(s). r_sign will be set to |
300,38 → 314,31
// up to something. |
initial r_sign = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
r_sign <= 1'b0; |
else if (pre_sign) |
r_sign <= ((r_divisor[(BW-1)])^(r_dividend[(BW-1)])); |
else if (r_busy) |
r_sign <= (r_sign)&&(!zero_divisor); |
else |
r_sign <= 1'b0; |
if (pre_sign) |
r_sign <= ((r_divisor[(2*BW-2)])^(r_dividend[(BW-1)])); |
else if (r_busy) |
r_sign <= (r_sign)&&(!zero_divisor); |
else |
r_sign <= 1'b0; |
|
initial o_quotient = 0; |
always @(posedge i_clk) |
if (i_reset) |
o_quotient <= 0; |
else if (r_busy) |
begin |
o_quotient <= { o_quotient[(BW-2):0], 1'b0 }; |
if (!diff[BW]) |
o_quotient[0] <= 1'b1; |
end else if (r_sign) |
o_quotient <= -o_quotient; |
else |
o_quotient <= 0; |
if (r_busy) |
begin |
o_quotient <= { o_quotient[(BW-2):0], 1'b0 }; |
if ((r_divisor[(2*BW-2):(BW)] == 0)&&(!diff[BW])) |
begin |
o_quotient[0] <= 1'b1; |
end |
end else if (r_sign) |
o_quotient <= -o_quotient; |
else |
o_quotient <= 0; |
|
// Set Carry on an exact divide |
// Perhaps nothing uses this, but ... well, I suppose we could remove |
// this logic eventually, just ... not yet. |
initial r_c = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
r_c <= 1'b0; |
else |
r_c <= (r_busy)&&(diff == 0); |
r_c <= (r_busy)&&((diff == 0)||(r_dividend == 0)); |
|
// The last flag: Negative. This flag is set assuming that the result |
// of the divide was negative (i.e., the high order bit is set). This |
342,215 → 349,4
assign w_n = o_quotient[(BW-1)]; |
|
assign o_flags = { 1'b0, w_n, r_c, r_z }; |
|
`ifdef FORMAL |
reg f_past_valid; |
initial f_past_valid = 0; |
always @(posedge i_clk) |
f_past_valid <= 1'b1; |
|
`ifdef DIV |
`define ASSUME assume |
`else |
`define ASSUME assert |
`endif |
|
initial `ASSUME(i_reset); |
always @(*) |
if (!f_past_valid) |
`ASSUME(i_reset); |
|
always @(posedge i_clk) |
if ((!f_past_valid)||($past(i_reset))) |
begin |
assert(!o_busy); |
assert(!o_valid); |
assert(!o_err); |
// |
assert(!r_busy); |
// assert(!zero_divisor); |
assert(r_bit==0); |
assert(!last_bit); |
assert(!pre_sign); |
// assert(!r_z); |
// assert(r_dividend==0); |
assert(o_quotient==0); |
assert(!r_c); |
assert(r_divisor==0); |
|
`ASSUME(!i_wr); |
end |
|
always @(*) |
if (o_busy) |
`ASSUME(!i_wr); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(o_busy))&&(!o_busy)) |
begin |
assert(o_valid); |
end |
|
// A formal methods section |
// |
// This section isn't yet complete. For now, it is just |
// a description of things I think should be in here ... not |
// yet a description of what it would take to prove |
// this divide (yet). |
always @(*) |
if (o_err) |
assert(o_valid); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_wr))) |
assert(!pre_sign); |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wr))&&($past(i_signed)) |
&&(|$past({i_numerator[BW-1],i_denominator[BW-1]}))) |
assert(pre_sign); |
|
// always @(posedge i_clk) |
// if ((f_past_valid)&&(!$past(pre_sign))) |
// assert(!r_sign); |
reg [BW:0] f_bits_set; |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wr))) |
assert(o_busy); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_valid))) |
assert(!o_valid); |
|
always @(*) |
if ((o_valid)&&(!o_err)) |
assert(r_z == ((o_quotient == 0)? 1'b1:1'b0)); |
else if (o_busy) |
assert(r_z == (((o_quotient&f_bits_set[BW-1:0]) == 0)? 1'b1: 1'b0)); |
|
always @(*) |
if ((o_valid)&&(!o_err)) |
assert(w_n == o_quotient[BW-1]); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(r_busy))&&(!$past(i_wr))) |
assert(!o_busy); |
always @(posedge i_clk) |
assert((!o_busy)||(!o_valid)); |
|
always @(*) |
if(r_busy) |
assert(o_busy); |
|
always @(posedge i_clk) |
if (i_reset) |
f_bits_set <= 0; |
else if (i_wr) |
f_bits_set <= 0; |
else if ((r_busy)&&(!pre_sign)) |
f_bits_set <= { f_bits_set[BW-1:0], 1'b1 }; |
|
always @(posedge i_clk) |
if (r_busy) |
assert(((1<<r_bit)-1) == f_bits_set); |
|
always @(*) |
if ((o_valid)&&(!o_err)) |
assert((!f_bits_set[BW])&&(&f_bits_set[BW-1:0])); |
|
|
/* |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(r_busy)) |
&&($past(r_divisor[2*BW-2:BW])==0)) |
begin |
if ($past(r_divisor) == 0) |
assert(o_err); |
else if ($past(pre_sign)) |
begin |
if ($past(r_dividend[BW-1])) |
assert(r_dividend == -$past(r_dividend)); |
if ($past(r_divisor[(2*BW-2)])) |
begin |
assert(r_divisor[(2*BW-2):(BW-1)] |
== -$past(r_divisor[(2*BW-2):(BW-1)])); |
assert(r_divisor[BW-2:0] == 0); |
end |
end else begin |
if (o_quotient[0]) |
assert(r_dividend == $past(diff)); |
else |
assert(r_dividend == $past(r_dividend)); |
|
// r_divisor should shift down on every step |
assert(r_divisor[2*BW-2]==0); |
assert(r_divisor[2*BW-3:0]==$past(r_divisor[2*BW-2:1])); |
end |
if ($past(r_dividend) >= $past(r_divisor[BW-1:0])) |
assert(o_quotient[0]); |
else |
assert(!o_quotient[0]); |
end |
*/ |
|
always @(*) |
if (r_busy) |
assert((f_bits_set & r_dividend[BW-1:0])==0); |
|
always @(*) |
if (r_busy) |
assert((r_divisor == 0) == zero_divisor); |
|
`ifdef VERIFIC |
// Verify unsigned division |
assert property (@(posedge i_clk) |
disable iff (i_reset) |
(i_wr)&&(i_denominator != 0)&&(!i_signed) |
|=> ((!o_err)&&(!o_valid)&&(o_busy)&&(!r_sign)&&(!pre_sign) |
throughout (r_bit == 0) |
##1 ((r_bit == $past(r_bit)+1)&&({1'b0,r_bit}< BW-1)) |
[*0:$] |
##1 ({ 1'b0, r_bit } == BW-1)) |
##1 (!o_err)&&(o_valid)); |
|
// Verify division by zero |
assert property (@(posedge i_clk) |
disable iff (i_reset) |
(i_wr)&&(i_denominator == 0) |
|=> (zero_divisor throughout |
(!o_err)&&(!o_valid)&&(pre_sign) [*0:1] |
##1 ((r_busy)&&(!o_err)&&(!o_valid)) |
##1 ((o_err)&&(o_valid)))); |
|
|
`endif // VERIFIC |
`endif |
endmodule |
// |
// How much logic will this divide use, now that it's been updated to |
// a different (long division) algorithm? |
// |
// iCE40 stats (Updated) (Original) |
// Number of cells: 700 820 |
// SB_CARRY 125 125 |
// SB_DFF 1 |
// SB_DFFE 33 1 |
// SB_DFFESR 37 |
// SB_DFFESS 31 |
// SB_DFFSR 40 40 |
// SB_LUT4 433 553 |
// |
// Xilinx stats (Updated) (Original) |
// Number of cells: 758 831 |
// FDRE 142 142 |
// LUT1 97 97 |
// LUT2 69 174 |
// LUT3 6 5 |
// LUT4 1 6 |
// LUT5 68 35 |
// LUT6 94 98 |
// MUXCY 129 129 |
// MUXF7 12 8 |
// MUXF8 6 3 |
// XORCY 134 134 |
|
/core/idecode.v
19,7 → 19,7
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2019, Gisselquist Technology, LLC |
// Copyright (C) 2015-2017, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
43,60 → 43,41
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
// |
`define CPU_SP_REG 4'hd |
`define CPU_CC_REG 4'he |
`define CPU_PC_REG 4'hf |
// |
`define CISBIT 31 |
`define CISIMMSEL 23 |
`define IMMSEL 18 |
`include "cpudefs.v" |
// |
// |
// |
module idecode(i_clk, i_reset, i_ce, i_stalled, |
module idecode(i_clk, i_rst, i_ce, i_stalled, |
i_instruction, i_gie, i_pc, i_pf_valid, |
i_illegal, |
o_valid, |
o_phase, o_illegal, |
o_pc, |
o_dcdR, o_dcdA, o_dcdB, |
o_preA, o_preB, |
o_I, o_zI, |
o_pc, o_gie, |
o_dcdR, o_dcdA, o_dcdB, o_I, o_zI, |
o_cond, o_wF, |
o_op, o_ALU, o_M, o_DV, o_FP, o_break, o_lock, |
o_wR, o_rA, o_rB, |
o_early_branch, o_early_branch_stb, o_branch_pc, o_ljmp, |
o_early_branch, o_branch_pc, o_ljmp, |
o_pipe, |
o_sim, o_sim_immv |
`ifdef FORMAL |
, f_insn_word, f_insn_gie |
`endif |
); |
parameter ADDRESS_WIDTH=24; |
parameter [0:0] OPT_MPY = 1'b1; |
parameter [0:0] OPT_EARLY_BRANCHING = 1'b1; |
parameter [0:0] OPT_PIPELINED = 1'b1; |
parameter [0:0] OPT_DIVIDE = (OPT_PIPELINED); |
parameter [0:0] OPT_FPU = 1'b0; |
parameter [0:0] OPT_CIS = 1'b1; |
parameter [0:0] OPT_LOCK = (OPT_PIPELINED); |
parameter [0:0] OPT_OPIPE = (OPT_PIPELINED); |
parameter [0:0] OPT_SIM = 1'b0; |
parameter [0:0] OPT_NO_USERMODE = 1'b0; |
localparam AW = ADDRESS_WIDTH; |
// |
input wire i_clk, i_reset, i_ce, i_stalled; |
input wire [31:0] i_instruction; |
input wire i_gie; |
input wire [(AW+1):0] i_pc; |
input wire i_pf_valid, i_illegal; |
parameter ADDRESS_WIDTH=24, IMPLEMENT_MPY=1, EARLY_BRANCHING=1, |
IMPLEMENT_DIVIDE=1, IMPLEMENT_FPU=0, AW = ADDRESS_WIDTH; |
input i_clk, i_rst, i_ce, i_stalled; |
input [31:0] i_instruction; |
input i_gie; |
input [(AW-1):0] i_pc; |
input i_pf_valid, i_illegal; |
output wire o_valid, o_phase; |
output reg o_illegal; |
output reg [(AW+1):0] o_pc; |
output reg [AW:0] o_pc; |
output reg o_gie; |
output reg [6:0] o_dcdR, o_dcdA, o_dcdB; |
output wire [4:0] o_preA, o_preB; |
output wire [31:0] o_I; |
output reg o_zI; |
output reg [3:0] o_cond; |
103,24 → 84,30
output reg o_wF; |
output reg [3:0] o_op; |
output reg o_ALU, o_M, o_DV, o_FP, o_break; |
output reg o_lock; |
output wire o_lock; |
output reg o_wR, o_rA, o_rB; |
output wire o_early_branch, o_early_branch_stb; |
output wire [(AW+1):0] o_branch_pc; |
output wire o_early_branch; |
output wire [(AW-1):0] o_branch_pc; |
output wire o_ljmp; |
output wire o_pipe; |
output reg o_sim /* verilator public_flat */; |
output reg [22:0] o_sim_immv /* verilator public_flat */; |
`ifdef FORMAL |
output reg [31:0] f_insn_word; |
output reg f_insn_gie; |
|
wire dcdA_stall, dcdB_stall, dcdF_stall; |
wire o_dcd_early_branch; |
wire [(AW-1):0] o_dcd_branch_pc; |
reg o_dcdI, o_dcdIz; |
`ifdef OPT_PIPELINED |
reg r_lock; |
`endif |
`ifdef OPT_PIPELINED_BUS_ACCESS |
reg r_pipe; |
`endif |
|
|
wire [4:0] w_op; |
wire w_ldi, w_mov, w_cmptst, w_ldilo, w_ALU, w_brev, |
w_noop, w_lock, w_sim, w_break, w_special, w_add, |
w_mpy; |
w_noop, w_lock; |
wire [4:0] w_dcdR, w_dcdB, w_dcdA; |
wire w_dcdR_pc, w_dcdR_cc; |
wire w_dcdA_pc, w_dcdA_cc; |
130,103 → 117,91
wire w_wR, w_rA, w_rB, w_wR_n; |
wire w_ljmp, w_ljmp_dly, w_cis_ljmp; |
wire [31:0] iword; |
wire pf_valid; |
|
assign pf_valid = (i_pf_valid)&&(!o_early_branch_stb); |
|
|
reg [14:0] r_nxt_half; |
|
generate if (OPT_CIS) |
begin : SET_IWORD |
|
assign iword = (o_phase) |
`ifdef OPT_CIS |
reg [15:0] r_nxt_half; |
assign iword = (o_phase) |
// set second half as a NOOP ... but really |
// shouldn't matter |
? { 1'b1, r_nxt_half[14:0], i_instruction[15:0] } |
? { r_nxt_half[15:0], i_instruction[15:0] } |
: i_instruction; |
end else begin : CLR_IWORD |
assign iword = { 1'b0, i_instruction[30:0] }; |
`else |
assign iword = { 1'b0, i_instruction[30:0] }; |
`endif |
|
// verilator lint_off UNUSED |
wire [14:0] unused_nxt_half; |
assign unused_nxt_half = r_nxt_half; |
// verilator lint_on UNUSED |
end endgenerate |
|
generate |
if (OPT_EARLY_BRANCHING) |
if (EARLY_BRANCHING != 0) |
begin |
if (OPT_CIS) |
begin : CIS_EARLY_BRANCHING |
`ifdef OPT_CIS |
reg r_pre_ljmp; |
always @(posedge i_clk) |
if ((i_rst)||(o_early_branch)) |
r_pre_ljmp <= 1'b0; |
else if ((i_ce)&&(i_pf_valid)) |
r_pre_ljmp <= (!o_phase)&&(i_instruction[31]) |
&&(i_instruction[14:0] == 15'h7cf8); |
else if (i_ce) |
r_pre_ljmp <= 1'b0; |
|
assign w_cis_ljmp = (o_phase)&&(iword[31:16] == 16'hfcf8); |
|
end else begin : NOCIS_EARLY_BRANCH |
|
assign w_cis_ljmp = 1'b0; |
|
end |
|
assign w_cis_ljmp = r_pre_ljmp; |
`else |
assign w_cis_ljmp = 1'b0; |
`endif |
// 0.1111.10010.000.1.1111.000000000... |
// 0111.1100.1000.0111.11000.... |
assign w_ljmp = (iword == 32'h7c87c000); |
|
end else begin : NO_EARLY_BRANCHING |
|
end else begin |
assign w_cis_ljmp = 1'b0; |
assign w_ljmp = 1'b0; |
end endgenerate |
end |
endgenerate |
|
`ifdef OPT_CIS |
`ifdef VERILATOR |
wire [4:0] w_cis_op; |
always @(iword) |
if (!iword[31]) |
w_cis_op = w_op; |
else case(iword[26:24]) |
3'h0: w_cis_op = 5'h00; |
3'h1: w_cis_op = 5'h01; |
3'h2: w_cis_op = 5'h02; |
3'h3: w_cis_op = 5'h10; |
3'h4: w_cis_op = 5'h12; |
3'h5: w_cis_op = 5'h13; |
3'h6: w_cis_op = 5'h18; |
3'h7: w_cis_op = 5'h0d; |
endcase |
`else |
reg [4:0] w_cis_op; |
|
generate if (OPT_CIS) |
begin : GEN_CIS_OP |
|
always @(*) |
if (!iword[`CISBIT]) |
w_cis_op = iword[26:22]; |
always @(iword,w_op) |
if (!iword[31]) |
w_cis_op <= w_op; |
else case(iword[26:24]) |
3'h0: w_cis_op = 5'h00; // ADD |
3'h1: w_cis_op = 5'h01; // AND |
3'h2: w_cis_op = 5'h02; // SUB |
3'h3: w_cis_op = 5'h10; // BREV |
3'h4: w_cis_op = 5'h12; // LW |
3'h5: w_cis_op = 5'h13; // SW |
3'h6: w_cis_op = 5'h18; // LDI |
3'h7: w_cis_op = 5'h0d; // MOV |
3'h0: w_cis_op <= 5'h00; |
3'h1: w_cis_op <= 5'h01; |
3'h2: w_cis_op <= 5'h02; |
3'h3: w_cis_op <= 5'h10; |
3'h4: w_cis_op <= 5'h12; |
3'h5: w_cis_op <= 5'h13; |
3'h6: w_cis_op <= 5'h18; |
3'h7: w_cis_op <= 5'h0d; |
endcase |
`endif |
`else |
wire [4:0] w_cis_op; |
assign w_cis_op = w_op; |
`endif |
|
end else begin : GEN_NOCIS_OP |
|
always @(*) |
w_cis_op = w_op; |
|
end endgenerate |
|
// Decode instructions |
assign w_op= iword[26:22]; |
assign w_mov = (w_cis_op == 5'h0d); |
assign w_ldi = (w_cis_op[4:1] == 4'hc); |
assign w_brev = (w_cis_op == 5'h08); |
assign w_mpy = (w_cis_op[4:1] == 4'h5)||(w_cis_op[4:0]==5'h0c); |
assign w_brev = (w_cis_op == 5'h8); |
assign w_cmptst = (w_cis_op[4:1] == 4'h8); |
assign w_ldilo = (w_cis_op[4:0] == 5'h09); |
assign w_ldilo = (w_cis_op[4:0] == 5'h9); |
assign w_ALU = (!w_cis_op[4]) // anything with [4]==0, but ... |
&&(w_cis_op[3:1] != 3'h7); // not the divide |
assign w_add = (w_cis_op[4:0] == 5'h02); |
assign w_mem = (w_cis_op[4:3] == 2'b10)&&(w_cis_op[2:1] !=2'b00); |
assign w_sto = (w_mem)&&( w_cis_op[0]); |
assign w_div = (!iword[`CISBIT])&&(w_op[4:1] == 4'h7); |
assign w_fpu = (!iword[`CISBIT])&&(w_op[4:3] == 2'b11) |
&&(w_dcdR[3:1] != 3'h7)&&(w_op[2:1] != 2'b00); |
// If the result register is either CC or PC, and this would otherwise |
// be a floating point instruction with floating point opcode of 0, |
// then this is a NOOP. |
assign w_special= (!iword[`CISBIT])&&((!OPT_FPU)||(w_dcdR[3:1]==3'h7)) |
&&(w_op[4:2] == 3'b111); |
assign w_break = (w_special)&&(w_op[4:0]==5'h1c); |
assign w_lock = (w_special)&&(w_op[4:0]==5'h1d); |
assign w_sim = (w_special)&&(w_op[4:0]==5'h1e); |
assign w_noop = (w_special)&&(w_op[4:0]==5'h1f); |
|
|
// w_dcdR (4 LUTs) |
237,14 → 212,25
// moves in iword[18] but only for the supervisor, and the other |
// four bits encoded in the instruction. |
// |
assign w_dcdR = { ((!iword[`CISBIT])&&(!OPT_NO_USERMODE)&&(w_mov)&&(!i_gie))?iword[`IMMSEL]:i_gie, |
assign w_dcdR = { ((!iword[31])&&(w_mov)&&(~i_gie))?iword[18]:i_gie, |
iword[30:27] }; |
// 2 LUTs |
// |
// If the result register is either CC or PC, and this would otherwise |
// be a floating point instruction with floating point opcode of 0, |
// then this is a NOOP. |
assign w_lock = (!iword[31])&&(w_op[4:0]==5'h1d)&&( |
((IMPLEMENT_FPU>0)&&(w_dcdR[3:1]==3'h7)) |
||(IMPLEMENT_FPU==0)); |
assign w_noop = (!iword[31])&&(w_op[4:0] == 5'h1f)&&( |
((IMPLEMENT_FPU>0)&&(w_dcdR[3:1] == 3'h7)) |
||(IMPLEMENT_FPU==0)); |
|
// dcdB - What register is used in the opB? |
// |
assign w_dcdB[4] = ((!iword[`CISBIT])&&(w_mov)&&(!OPT_NO_USERMODE)&&(!i_gie))?iword[13]:i_gie; |
assign w_dcdB[3:0]= (iword[`CISBIT]) |
? (((!iword[`CISIMMSEL])&&(iword[26:25]==2'b10)) |
assign w_dcdB[4] = ((!iword[31])&&(w_mov)&&(~i_gie))?iword[13]:i_gie; |
assign w_dcdB[3:0]= (iword[31]) |
? (((!iword[23])&&(iword[26:25]==2'b10)) |
? `CPU_SP_REG : iword[22:19]) |
: iword[17:14]; |
|
260,18 → 246,26
assign w_dcdB_pc = (w_rB)&&(w_dcdB[3:0] == `CPU_PC_REG); |
assign w_dcdB_cc = (w_rB)&&(w_dcdB[3:0] == `CPU_CC_REG); |
|
// Under what condition will we execute this |
// instruction? Only the load immediate instruction |
// is completely unconditional. |
// |
// Under what condition will we execute this instruction? Only the |
// load immediate instruction and the CIS instructions are completely |
// unconditional. Well ... not quite. The BREAK, LOCK, and SIM/NOOP |
// instructions are also unconditional. |
// |
assign w_cond = ((w_ldi)||(w_special)||(iword[`CISBIT])) ? 4'h8 : |
// 3+4 LUTs |
assign w_cond = ((w_ldi)||(iword[31])) ? 4'h8 : |
{ (iword[21:19]==3'h0), iword[21:19] }; |
|
// 1 LUT |
assign w_mem = (w_cis_op[4:3] == 2'b10)&&(w_cis_op[2:1] !=2'b00); |
assign w_sto = (w_mem)&&( w_cis_op[0]); |
// 1 LUT |
assign w_div = (!iword[31])&&(w_op[4:1] == 4'h7); |
// 2 LUTs |
assign w_fpu = (!iword[31])&&(w_op[4:3] == 2'b11) |
&&(w_dcdR[3:1] != 3'h7)&&(w_op[2:1] != 2'b00); |
// |
// rA - do we need to read register A? |
assign w_rA = // Floating point reads reg A |
((w_fpu)&&(OPT_FPU)) |
((w_fpu)&&(w_cis_op[4:1] != 4'hf)) |
// Divide's read A |
||(w_div) |
// ALU ops read A, |
284,24 → 278,25
// rB -- do we read a register for operand B? Specifically, do we |
// add the registers value to the immediate to create opB? |
assign w_rB = (w_mov) |
||((!iword[`CISBIT])&&(iword[`IMMSEL])&&(!w_ldi)&&(!w_special)) |
||(( iword[`CISBIT])&&(iword[`CISIMMSEL])&&(!w_ldi)) |
||((!iword[31])&&(iword[18])&&(!w_ldi)) |
||(( iword[31])&&(iword[23])&&(!w_ldi)) |
// If using compressed instruction sets, |
// we *always* read on memory operands. |
||(( iword[`CISBIT])&&(w_mem)); |
|
||(( iword[31])&&(w_mem)); |
// wR -- will we be writing our result back? |
// wR_n = !wR |
// All but STO, NOOP/BREAK/LOCK, and CMP/TST write back to w_dcdR |
// 1 LUT: All but STO, NOOP/BREAK/LOCK, and CMP/TST write back to w_dcdR |
assign w_wR_n = (w_sto) |
||(w_special) |
||((!iword[31])&&(w_cis_op[4:3]==2'b11) |
&&(w_cis_op[2:1]!=2'b00) |
&&(w_dcdR[3:1]==3'h7)) |
||(w_cmptst); |
assign w_wR = !w_wR_n; |
assign w_wR = ~w_wR_n; |
// |
// wF -- do we write flags when we are done? |
// |
assign w_wF = (w_cmptst) |
||((w_cond[3])&&(((w_fpu)&&(OPT_FPU))||(w_div) |
||((w_cond[3])&&((w_fpu)||(w_div) |
||((w_ALU)&&(!w_mov)&&(!w_ldilo)&&(!w_brev) |
&&(w_dcdR[3:1] != 3'h7)))); |
|
318,33 → 313,27
// MOVE immediates have one less bit |
:((w_mov) ?{ {(23-13){iword[12]}}, iword[12:0] } |
// Normal Op-B immediate ... 18 or 14 bits |
:((!iword[`IMMSEL]) ? { {(23-18){iword[17]}}, iword[17:0] } |
:((~iword[18]) ? { {(23-18){iword[17]}}, iword[17:0] } |
: { {(23-14){iword[13]}}, iword[13:0] } |
)); |
|
generate if (OPT_CIS) |
begin : GEN_CIS_IMMEDIATE |
wire [7:0] w_halfbits; |
assign w_halfbits = iword[`CISIMMSEL:16]; |
`ifdef OPT_CIS |
wire [7:0] w_halfbits; |
assign w_halfbits = iword[23:16]; |
|
wire [7:0] w_halfI; |
assign w_halfI = (iword[26:24]==3'h6) ? w_halfbits[7:0] // 8'b for LDI |
:(w_halfbits[7])? |
{ {(6){w_halfbits[2]}}, w_halfbits[1:0]} |
:{ w_halfbits[6], w_halfbits[6:0] }; |
assign w_I = (iword[`CISBIT]) |
? {{(23-8){w_halfI[7]}}, w_halfI } |
: w_fullI; |
|
end else begin : GEN_NOCIS_IMMEDIATE |
|
assign w_I = w_fullI; |
|
end endgenerate |
|
wire [7:0] w_halfI; |
assign w_halfI = (iword[26:24]==3'h6) ? w_halfbits[7:0] |
:(w_halfbits[7])? |
{ {(6){w_halfbits[2]}}, w_halfbits[1:0]} |
:{ w_halfbits[6], w_halfbits[6:0] }; |
assign w_I = (iword[31])?{{(23-8){w_halfI[7]}}, w_halfI }:w_fullI; |
`else |
assign w_I = w_fullI; |
`endif |
assign w_Iz = (w_I == 0); |
|
|
`ifdef OPT_CIS |
// |
// The o_phase parameter is special. It needs to let the software |
// following know that it cannot break/interrupt on an o_phase asserted |
352,276 → 341,213
// half of a CIS instruction. To do this, o_phase must be asserted |
// when the first instruction half is valid, but not asserted on either |
// a 32-bit instruction or the second half of a 2x16-bit instruction. |
generate if (OPT_CIS) |
begin : GEN_CIS_PHASE |
reg r_phase; |
|
// Phase is '1' on the first instruction of a two-part set |
// But, due to the delay in processing, it's '1' when our |
// output is valid for that first part, but that'll be the |
// same time we are processing the second part ... so it may |
// look to us like a '1' on the second half of processing. |
|
// When no instruction is in the pipe, phase is zero |
initial r_phase = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(w_ljmp_dly)) |
reg r_phase; |
initial r_phase = 1'b0; |
always @(posedge i_clk) |
if ((i_rst) // When no instruction is in the pipe, phase is zero |
||(o_early_branch)||(w_ljmp_dly)) |
r_phase <= 1'b0; |
else if ((i_ce)&&(pf_valid)) |
begin |
if (o_phase) |
// CIS instructions only have two parts. On |
// the second part (o_phase is true), return |
// back to the first |
r_phase <= 0; |
else |
r_phase <= (i_instruction[`CISBIT])&&(!i_illegal); |
end else if (i_ce) |
else if ((i_ce)&&(i_pf_valid)) |
r_phase <= (o_phase)? 1'b0 |
: ((i_instruction[31])&&(i_pf_valid)); |
else if (i_ce) |
r_phase <= 1'b0; |
// Phase is '1' on the first instruction of a two-part set |
// But, due to the delay in processing, it's '1' when our output is |
// valid for that first part, but that'll be the same time we |
// are processing the second part ... so it may look to us like a '1' |
// on the second half of processing. |
|
assign o_phase = r_phase; |
end else begin |
assign o_phase = 1'b0; |
end endgenerate |
assign o_phase = r_phase; |
`else |
assign o_phase = 1'b0; |
`endif |
|
|
initial o_illegal = 1'b0; |
always @(posedge i_clk) |
if (i_ce) |
begin |
if (OPT_PIPELINED) |
o_illegal <= ((i_illegal) |
&&((!o_phase)||(!o_valid))) |
||((o_illegal)&&(o_phase)&&(o_valid)); |
else |
o_illegal <= (i_illegal)&&(!o_phase); |
if ((!OPT_CIS)&&(i_instruction[`CISBIT])) |
o_illegal <= 1'b1; |
if ((!OPT_MPY)&&(w_mpy)) |
o_illegal <= 1'b1; |
if (i_rst) |
o_illegal <= 1'b0; |
else if (i_ce) |
begin |
`ifdef OPT_CIS |
o_illegal <= (i_illegal); |
`else |
o_illegal <= ((i_illegal) || (i_instruction[31])); |
`endif |
if ((IMPLEMENT_MPY==0)&&((w_cis_op[4:1]==4'h5)||(w_cis_op[4:0]==5'h0c))) |
o_illegal <= 1'b1; |
|
if ((!OPT_DIVIDE)&&(w_div)) |
o_illegal <= 1'b1; |
else if ((OPT_DIVIDE)&&(w_div)&&(w_dcdR[3:1]==3'h7)) |
o_illegal <= 1'b1; |
if ((IMPLEMENT_DIVIDE==0)&&(w_div)) |
o_illegal <= 1'b1; |
else if ((IMPLEMENT_DIVIDE!=0)&&(w_div)&&(w_dcdR[3:1]==3'h7)) |
o_illegal <= 1'b1; |
|
|
if ((!OPT_FPU)&&(w_fpu)) |
o_illegal <= 1'b1; |
if ((IMPLEMENT_FPU==0)&&(w_fpu)) |
o_illegal <= 1'b1; |
|
if ((!OPT_SIM)&&(w_sim)) |
// Simulation instructions on real hardware should |
// always cause an illegal instruction error |
o_illegal <= 1'b1; |
if ((w_cis_op[4:3]==2'b11)&&(w_cis_op[2:1]!=2'b00) |
&&(w_dcdR[3:1]==3'h7) |
&&( |
(w_cis_op[2:0] != 3'h4) // BREAK |
`ifdef OPT_PIPELINED |
&&(w_cis_op[2:0] != 3'h5) // LOCK |
`endif |
// SIM instructions are always illegal |
&&(w_cis_op[2:0] != 3'h7))) // NOOP |
o_illegal <= 1'b1; |
end |
|
// There are two (missing) special instructions |
// These should cause an illegal instruction error |
if ((w_dcdR[3:1]==3'h7)&&(w_cis_op[4:1]==4'b1101)) |
o_illegal <= 1'b1; |
|
// If the lock function isn't implemented, this should |
// also cause an illegal instruction error |
if ((!OPT_LOCK)&&(w_lock)) |
o_illegal <= 1'b1; |
end |
|
initial o_pc = 0; |
always @(posedge i_clk) |
if ((i_ce)&&((o_phase)||(i_pf_valid))) |
begin |
o_pc[0] <= 1'b0; |
if (i_ce) |
begin |
`ifdef OPT_CIS |
if (!o_phase) |
o_gie<= i_gie; |
|
if (OPT_CIS) |
begin |
if (iword[`CISBIT]) |
if (iword[31]) |
begin |
if (o_phase) |
o_pc[AW+1:1] <= o_pc[AW+1:1] + 1'b1; |
else |
o_pc <= { i_pc[AW+1:2], 1'b1, 1'b0 }; |
o_pc <= o_pc + 1'b1; |
else if (i_pf_valid) |
o_pc <= { i_pc, 1'b1 }; |
end else begin |
// The normal, non-CIS case |
o_pc <= { i_pc[AW+1:2] + 1'b1, 2'b00 }; |
o_pc <= { i_pc + 1'b1, 1'b0 }; |
end |
end else begin |
// The normal, non-CIS case |
o_pc <= { i_pc[AW+1:2] + 1'b1, 2'b00 }; |
end |
end |
`else |
o_gie<= i_gie; |
o_pc <= { i_pc + 1'b1, 1'b0 }; |
`endif |
|
initial o_dcdR = 0; |
initial o_dcdA = 0; |
initial o_dcdB = 0; |
initial o_DV = 0; |
initial o_FP = 0; |
initial o_lock = 0; |
always @(posedge i_clk) |
if (i_ce) |
begin |
// Under what condition will we execute this |
// instruction? Only the load immediate instruction |
// is completely unconditional. |
o_cond <= w_cond; |
// Don't change the flags on conditional instructions, |
// UNLESS: the conditional instruction was a CMP |
// or TST instruction. |
o_wF <= w_wF; |
// Under what condition will we execute this |
// instruction? Only the load immediate instruction |
// is completely unconditional. |
o_cond <= w_cond; |
// Don't change the flags on conditional instructions, |
// UNLESS: the conditional instruction was a CMP |
// or TST instruction. |
o_wF <= w_wF; |
|
// Record what operation/op-code (4-bits) we are doing |
// Note that LDI magically becomes a MOV |
// instruction here. That way it's a pass through |
// the ALU. Likewise, the two compare instructions |
// CMP and TST becomes SUB and AND here as well. |
// We keep only the bottom four bits, since we've |
// already done the rest of the decode necessary to |
// settle between the other instructions. For example, |
// o_FP plus these four bits uniquely defines the FP |
// instruction, o_DV plus the bottom of these defines |
// the divide, etc. |
o_op <= w_cis_op[3:0]; |
if ((w_ldi)||(w_noop)||(w_lock)) |
o_op <= 4'hd; |
// Record what operation/op-code (4-bits) we are doing |
// Note that LDI magically becomes a MOV |
// instruction here. That way it's a pass through |
// the ALU. Likewise, the two compare instructions |
// CMP and TST becomes SUB and AND here as well. |
// We keep only the bottom four bits, since we've |
// already done the rest of the decode necessary to |
// settle between the other instructions. For example, |
// o_FP plus these four bits uniquely defines the FP |
// instruction, o_DV plus the bottom of these defines |
// the divide, etc. |
o_op <= ((w_ldi)||(w_noop))? 4'hd : w_cis_op[3:0]; |
|
// Default values |
o_dcdR <= { w_dcdR_cc, w_dcdR_pc, w_dcdR}; |
o_dcdA <= { w_dcdA_cc, w_dcdA_pc, w_dcdA}; |
o_dcdB <= { w_dcdB_cc, w_dcdB_pc, w_dcdB}; |
o_wR <= w_wR; |
o_rA <= w_rA; |
o_rB <= w_rB; |
r_I <= w_I; |
o_zI <= w_Iz; |
// Default values |
o_dcdR <= { w_dcdR_cc, w_dcdR_pc, w_dcdR}; |
o_dcdA <= { w_dcdA_cc, w_dcdA_pc, w_dcdA}; |
o_dcdB <= { w_dcdB_cc, w_dcdB_pc, w_dcdB}; |
o_wR <= w_wR; |
o_rA <= w_rA; |
o_rB <= w_rB; |
r_I <= w_I; |
o_zI <= w_Iz; |
|
// Turn a NOOP into an ALU operation--subtract in |
// particular, although it doesn't really matter as long |
// as it doesn't take longer than one clock. Note |
// also that this depends upon not setting any registers |
// or flags, which should already be true. |
o_ALU <= (w_ALU)||(w_ldi)||(w_cmptst)||(w_noop) |
||((!OPT_LOCK)&&(w_lock)); |
o_M <= w_mem; |
o_DV <= (OPT_DIVIDE)&&(w_div); |
o_FP <= (OPT_FPU)&&(w_fpu); |
// Turn a NOOP into an ALU operation--subtract in |
// particular, although it doesn't really matter as long |
// as it doesn't take longer than one clock. Note |
// also that this depends upon not setting any registers |
// or flags, which should already be true. |
o_ALU <= (w_ALU)||(w_ldi)||(w_cmptst)||(w_noop); |
o_M <= w_mem; |
o_DV <= w_div; |
o_FP <= w_fpu; |
|
o_break <= w_break; |
o_lock <= (OPT_LOCK)&&(w_lock); |
o_break <= (!iword[31])&&(w_op[4:0]==5'h1c)&&( |
((IMPLEMENT_FPU>0)&&(w_dcdR[3:1]==3'h7)) |
||(IMPLEMENT_FPU==0)); |
`ifdef OPT_PIPELINED |
r_lock <= w_lock; |
`endif |
`ifdef OPT_CIS |
r_nxt_half <= { iword[31], iword[14:0] }; |
`endif |
|
if (OPT_CIS) |
r_nxt_half <= { iword[14:0] }; |
else |
r_nxt_half <= 0; |
|
if (OPT_SIM) |
begin |
`ifdef VERILATOR |
// Support the SIM instruction(s) |
o_sim <= (w_sim)||(w_noop); |
o_sim <= (!iword[31])&&(w_op[4:1] == 4'hf) |
&&(w_dcdR[3:1] == 3'h7); |
`else |
o_sim <= 1'b0; |
`endif |
o_sim_immv <= iword[22:0]; |
end else begin |
o_sim <= 1'b0; |
o_sim_immv <= 0; |
end |
end |
|
assign o_preA = w_dcdA; |
assign o_preB = w_dcdB; |
`ifdef OPT_PIPELINED |
assign o_lock = r_lock; |
`else |
assign o_lock = 1'b0; |
`endif |
|
generate if (OPT_EARLY_BRANCHING) |
begin : GEN_EARLY_BRANCH_LOGIC |
reg r_early_branch, |
r_early_branch_stb, |
r_ljmp; |
reg [(AW+1):0] r_branch_pc; |
generate |
if (EARLY_BRANCHING!=0) |
begin |
reg r_early_branch, r_ljmp; |
reg [(AW-1):0] r_branch_pc; |
|
initial r_ljmp = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
r_ljmp <= 1'b0; |
else if (i_ce) |
begin |
if ((r_ljmp)&&(pf_valid)) |
if (i_rst) |
r_ljmp <= 1'b0; |
else if (o_early_branch_stb) |
r_ljmp <= 1'b0; |
else if (pf_valid) |
begin |
if ((OPT_CIS)&&(iword[`CISBIT])) |
r_ljmp <= w_cis_ljmp; |
else |
r_ljmp <= (w_ljmp); |
end else if ((OPT_CIS)&&(o_phase)&&(iword[`CISBIT])) |
`ifdef OPT_CIS |
else if ((i_ce)&&(o_phase)) |
r_ljmp <= w_cis_ljmp; |
end |
`endif |
else if ((i_ce)&&(i_pf_valid)) |
r_ljmp <= (w_ljmp); |
assign o_ljmp = r_ljmp; |
|
initial r_early_branch = 1'b0; |
initial r_early_branch_stb = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
if (i_rst) |
r_early_branch <= 1'b0; |
else if ((i_ce)&&(i_pf_valid)) |
begin |
r_early_branch <= 1'b0; |
r_early_branch_stb <= 1'b0; |
end else if ((i_ce)&&(pf_valid)) |
begin |
if (r_ljmp) |
begin |
// LW (PC),PC |
r_early_branch <= 1'b1; |
r_early_branch_stb <= 1'b1; |
end else if ((!iword[`CISBIT])&&(iword[30:27]==`CPU_PC_REG) |
// LOD (PC),PC |
r_early_branch <= 1'b1; |
else if ((!iword[31])&&(iword[30:27]==`CPU_PC_REG) |
&&(w_cond[3])) |
begin |
if ((w_add)&&(!iword[`IMMSEL])) |
begin |
if ((w_op[4:0]==5'h02)&&(!iword[18])) |
// Add x,PC |
r_early_branch <= 1'b1; |
r_early_branch_stb <= 1'b1; |
end else begin |
else |
r_early_branch <= 1'b0; |
r_early_branch_stb <= 1'b0; |
end |
// LDI #x,PC is no longer supported |
end else begin |
end else |
r_early_branch <= 1'b0; |
r_early_branch_stb <= 1'b0; |
end |
end else if (i_ce) |
begin |
r_early_branch <= 1'b0; |
r_early_branch_stb <= 1'b0; |
end else |
r_early_branch_stb <= 1'b0; |
r_early_branch <= 1'b0; |
|
initial r_branch_pc = 0; |
always @(posedge i_clk) |
if (i_ce) |
begin |
if (r_ljmp) |
r_branch_pc <= { iword[(AW+1):2], |
2'b00 }; |
else begin |
// Add x,PC |
r_branch_pc[AW+1:2] <= i_pc[AW+1:2] |
+ {{(AW-15){iword[17]}},iword[16:2]} |
+ {{(AW-1){1'b0}},1'b1}; |
r_branch_pc[1:0] <= 2'b00; |
if (i_ce) |
begin |
if (r_ljmp) |
r_branch_pc <= iword[(AW+1):2]; |
else // Add x,PC |
r_branch_pc <= i_pc |
+ {{(AW-15){iword[17]}},iword[16:2]} |
+ {{(AW-1){1'b0}},1'b1}; |
end |
end |
|
assign w_ljmp_dly = r_ljmp; |
assign o_early_branch = r_early_branch; |
assign o_early_branch_stb = r_early_branch_stb; |
assign o_branch_pc = r_branch_pc; |
end else begin |
assign w_ljmp_dly = 1'b0; |
assign o_early_branch = 1'b0; |
assign o_early_branch_stb = 1'b0; |
assign o_branch_pc = {(AW+2){1'b0}}; |
assign o_early_branch = 1'b0; |
assign o_branch_pc = {(AW){1'b0}}; |
assign o_ljmp = 1'b0; |
|
// verilator lint_off UNUSED |
wire early_branch_unused; |
assign early_branch_unused = w_add; |
// verilator lint_on UNUSED |
end endgenerate |
|
|
635,64 → 561,14
// Note that we're not using iword here ... there's a lot of logic |
// taking place, and it's only valid if the new word is not compressed. |
// |
reg r_valid, r_insn_is_pipeable; |
generate if (OPT_OPIPE) |
begin : GEN_OPIPE |
reg r_pipe; |
|
wire [13:0] pipe_addr_diff; |
assign pipe_addr_diff = w_I[13:0] - r_I[13:0]; |
|
// Pipeline logic is too extreme for a single clock. |
// Let's break it into two clocks, using r_insn_is_pipeable |
// If this function is true, then the instruction associated |
// with the current output *may* have a pipeable instruction |
// following it. |
// |
initial r_insn_is_pipeable = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
r_insn_is_pipeable <= 1'b0; |
else if ((i_ce)&&((!pf_valid)||(i_illegal))&&(!o_phase)) |
// Pipeline bubble, can't pipe through it |
r_insn_is_pipeable <= 1'b0; |
else if (o_ljmp) |
r_insn_is_pipeable <= 1'b0; |
else if ((i_ce)&&((!OPT_CIS)&&(i_instruction[`CISBIT]))) |
r_insn_is_pipeable <= 1'b0; |
else if (i_ce) |
begin // This is a valid instruction |
r_insn_is_pipeable <= (w_mem)&&(w_rB) |
// PC (and CC) registers can change |
// underneath us. Therefore they cannot |
// be used as a base register for piped |
// memory ops |
&&(w_dcdB[3:1] != 3'h7) |
// Writes to PC or CC will destroy any |
// possibility of pipeing--since they |
// could create a jump |
&&(w_dcdR[3:1] != 3'h7) |
// |
// Loads landing in the current address |
// pointer register are not allowed, |
// as they could then be used to violate |
// our rule(s) |
&&((w_cis_op[0])||(w_dcdB != w_dcdA)); |
end // else |
// The pipeline is stalled |
|
|
initial r_pipe = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
r_pipe <= 1'b0; |
else if (i_ce) |
r_pipe <= ((pf_valid)||(o_phase)) |
// The last operation must be capable of |
// being followed by a pipeable memory op |
&&(r_insn_is_pipeable) |
reg r_valid; |
`ifdef OPT_PIPELINED_BUS_ACCESS |
initial r_pipe = 1'b0; |
always @(posedge i_clk) |
if (i_ce) |
r_pipe <= (r_valid)&&((i_pf_valid)||(o_phase)) |
// Both must be memory operations |
&&(w_mem) |
&&(w_mem)&&(o_M) |
// Both must be writes, or both stores |
&&(o_op[0] == w_cis_op[0]) |
// Both must be register ops |
699,1161 → 575,34
&&(w_rB) |
// Both must use the same register for B |
&&(w_dcdB[3:0] == o_dcdB[3:0]) |
// CC or PC registers are not valid addresses |
// Captured above |
// But ... the result can never be B |
// Captured above |
// |
// Reads to CC or PC not allowed |
// &&((o_op[0])||(w_dcdR[3:1] != 3'h7)) |
// Prior-reads to CC or PC not allowed |
// Captured above |
&&((o_op[0]) |
||(w_dcdB[3:0] != o_dcdA[3:0])) |
// Needs to be to the mode, supervisor or user |
&&(i_gie == o_gie) |
// Same condition, or no condition before |
&&((w_cond[2:0]==o_cond[2:0]) |
&&((i_instruction[21:19]==o_cond[2:0]) |
||(o_cond[2:0] == 3'h0)) |
// Same or incrementing immediate |
&&(w_I[13]==r_I[13]) |
&&(pipe_addr_diff <= 14'h4); |
assign o_pipe = r_pipe; |
end else begin |
assign o_pipe = 1'b0; |
always @(*) |
r_insn_is_pipeable = 1'b0; |
// Same immediate |
&&((w_I[13:2]==r_I[13:2]) |
||({1'b0, w_I[13:2]}==(r_I[13:2]+12'h1))); |
assign o_pipe = r_pipe; |
`else |
assign o_pipe = 1'b0; |
`endif |
|
// verilator lint_off UNUSED |
wire unused_pipable; |
assign unused_pipable = r_insn_is_pipeable; |
// verilator lint_on UNUSED |
end endgenerate |
|
initial r_valid = 1'b0; |
generate if (OPT_PIPELINED) |
begin : GEN_DCD_VALID |
|
always @(posedge i_clk) |
if (i_reset) |
r_valid <= 1'b0; |
else if (i_ce) |
r_valid <= ((pf_valid)||(o_phase))&&(!o_ljmp); |
else if (!i_stalled) |
r_valid <= 1'b0; |
|
end else begin : GEN_DCD_VALID |
|
always @(posedge i_clk) |
if (i_reset) |
always @(posedge i_clk) |
if (i_rst) |
r_valid <= 1'b0; |
else if (i_ce) |
r_valid <= ((i_pf_valid)||(o_phase)||(i_illegal)) |
&&(!o_ljmp)&&(!o_early_branch); |
else if (!i_stalled) |
r_valid <= ((pf_valid)||(o_phase))&&(!o_ljmp); |
else |
r_valid <= 1'b0; |
|
end endgenerate |
|
assign o_valid = r_valid; |
|
|
assign o_I = { {(32-22){r_I[22]}}, r_I[21:0] }; |
|
// Make Verilator happy across all our various options |
// verilator lint_off UNUSED |
wire [5:0] possibly_unused; |
assign possibly_unused = { w_lock, w_ljmp, w_ljmp_dly, w_cis_ljmp, i_pc[1:0] }; |
// verilator lint_on UNUSED |
`ifdef FORMAL |
reg f_past_valid; |
|
initial f_past_valid = 1'b0; |
always @(posedge i_clk) |
f_past_valid <= 1'b1; |
|
`define ASSERT assert |
`ifdef IDECODE |
`define ASSUME assume |
`else |
`define ASSUME assert |
`endif |
always @(posedge i_clk) |
if ((i_ce)&&(i_pf_valid)&&(!o_phase)) |
f_insn_word <= i_instruction; |
always @(posedge i_clk) |
if ((i_ce)&&(i_pf_valid)&&(!o_phase)) |
f_insn_gie = i_gie; |
always @(*) |
if (o_phase) |
assert(r_nxt_half == f_insn_word[14:0]); |
|
//////////////////////////// |
// |
// |
// Assumptions about our inputs |
// |
// |
/////////////////////////// |
always @(*) |
if (OPT_PIPELINED) |
`ASSUME(i_ce == ((!o_valid)||(!i_stalled))); |
else |
`ASSUME(i_ce == !i_stalled); |
|
always @(posedge i_clk) |
if ((!f_past_valid)||($past(i_reset))) |
begin |
`ASSERT(!o_valid); |
// `ASSERT(!o_illegal); |
`ASSERT(!o_phase); |
`ASSERT(!o_ljmp); |
`ASSERT(!o_pipe); |
|
`ASSUME(!i_pf_valid); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!i_reset)) |
`ASSUME(i_gie == $past(i_gie)); |
|
`ifdef IDECODE |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&(!$past(i_ce)) |
&&($past(f_past_valid))&&(!$past(i_reset,2))&&(!$past(i_ce,2))) |
assume(i_ce); |
`endif |
|
reg f_new_insn, f_last_insn; |
|
initial f_new_insn = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
f_new_insn <= 1'b0; |
else |
f_new_insn <= ((pf_valid)&&(!i_stalled)); |
|
initial f_last_insn = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
f_last_insn <= 1'b0; |
else |
f_last_insn <= (o_valid)&&(i_stalled); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(f_last_insn)&&(!i_reset)) |
begin |
if (($past(pf_valid))&&(pf_valid)) |
begin |
`ASSUME(i_instruction == $past(i_instruction)); |
`ASSUME(i_gie == $past(i_gie)); |
`ASSUME(i_pc == $past(i_pc)); |
`ASSUME(i_illegal == $past(i_illegal)); |
end |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(o_early_branch_stb)) |
`ASSUME(!pf_valid); |
|
always @(*) |
`ASSUME(i_pc[1:0] == 2'b00); |
always @(*) |
if ((o_valid)&&(!o_early_branch)) |
`ASSERT((o_illegal)||(o_pc[1] == o_phase)); |
|
wire [4+21+32+1+4+1+4+11+AW+3+23-1:0] f_result; |
assign f_result = { o_valid, o_phase, o_illegal, |
i_gie, o_dcdR, o_dcdA, o_dcdB, o_I, o_zI, o_cond, |
o_wF, o_op, o_ALU, o_M, o_DV, o_FP, o_break, o_lock, |
o_wR, o_rA, o_rB, o_early_branch, o_branch_pc, o_ljmp, |
o_pipe, o_sim, o_sim_immv, o_pc }; |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&(f_last_insn)) |
`ASSERT(f_result == $past(f_result)); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(pf_valid)) |
&&(!$past(o_ljmp))) |
`ASSERT((!OPT_PIPELINED)||(o_valid)); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(f_new_insn) |
&&($past(pf_valid))&&($past(i_illegal))&&(!$past(o_phase))) |
`ASSERT(o_illegal); |
|
`ifdef IDECODE |
// Let's walk through some basic instructions |
// First 8-instructions, SUB - ASR |
always @(*) |
if ((!iword[`CISBIT])&&(iword[26:25]==2'b00)) |
begin |
`ASSERT(!w_cmptst); |
`ASSERT(!w_div); |
`ASSERT(!w_mem); |
`ASSERT(!w_sto); |
`ASSERT(!w_ldi); |
`ASSERT(!w_mov); |
`ASSERT(!w_brev); |
`ASSERT(!w_ldilo); |
`ASSERT(!w_special); |
`ASSERT(!w_fpu); |
`ASSERT(!w_mpy); |
`ASSERT((w_rA)&&(w_wR)&&(w_ALU)); |
`ASSERT(w_rB == iword[`IMMSEL]); |
`ASSERT(w_dcdA[4] == i_gie); |
`ASSERT(w_dcdB[4] == i_gie); |
`ASSERT(w_dcdA[3:0] == iword[30:27]); |
`ASSERT(w_dcdB[3:0] == iword[17:14]); |
|
`ASSERT(w_cis_op == w_op); |
|
`ASSERT(w_cond[3] == (iword[21:19] == 3'b000)); |
`ASSERT(w_cond[2:0] == iword[21:19]); |
`ASSERT((w_wF == w_cond[3])||(w_dcdA[3:1]==3'b111)); |
end else if ((iword[`CISBIT])&&(iword[26:24]<3'b011)) |
begin |
`ASSERT(!w_cmptst); |
`ASSERT(!w_div); |
`ASSERT(!w_mem); |
`ASSERT(!w_sto); |
`ASSERT(!w_ldi); |
`ASSERT(!w_mov); |
`ASSERT(!w_brev); |
`ASSERT(!w_ldilo); |
`ASSERT(!w_special); |
`ASSERT(!w_fpu); |
`ASSERT(!w_mpy); |
`ASSERT((w_rA)&&(w_wR)&&(w_ALU)); |
`ASSERT(w_rB == iword[`CISIMMSEL]); |
`ASSERT(w_dcdA[4] == i_gie); |
`ASSERT(w_dcdB[4] == i_gie); |
`ASSERT(w_dcdA[3:0] == iword[30:27]); |
`ASSERT(w_dcdB[3:0] == iword[22:19]); |
|
if (iword[26:24] == 3'b000) |
`ASSERT(w_cis_op == 5'h0); |
else if (iword[26:24] == 5'h01) |
`ASSERT(w_cis_op == 5'h01); |
else // if (iword[26:24] == 3'b010) |
`ASSERT(w_cis_op == 5'h02); |
|
`ASSERT(w_cond == 4'h8); |
|
if (iword[`CISIMMSEL]) |
`ASSERT(w_I == { {(23-3){iword[18]}}, iword[18:16] }); |
else |
`ASSERT(w_I == { {(23-7){iword[22]}}, iword[22:16] }); |
end else |
`ASSERT(!w_add); |
|
// BREV and LDILO |
always @(*) |
if ((!iword[`CISBIT])&&((w_cis_op == 5'h8) |
||(w_cis_op == 5'h09))) |
begin |
`ASSERT(!w_mpy); |
`ASSERT(!w_div); |
`ASSERT(!w_cmptst); |
`ASSERT(!w_mem); |
`ASSERT(!w_sto); |
`ASSERT(!w_ldi); |
`ASSERT(!w_mov); |
if (w_cis_op == 5'h8) |
begin |
`ASSERT(w_brev); |
`ASSERT(!w_ldilo); |
`ASSERT((!w_rA)&&(w_wR)&&(w_ALU)); |
end else begin// if (w_cis_op == 5'h9) |
`ASSERT(w_ldilo); |
`ASSERT(!w_brev); |
`ASSERT((w_rA)&&(w_wR)&&(w_ALU)); |
end |
`ASSERT(!w_special); |
`ASSERT(!w_fpu); |
`ASSERT(w_rB == iword[`IMMSEL]); |
`ASSERT(w_dcdA[4] == i_gie); |
`ASSERT(w_dcdB[4] == i_gie); |
`ASSERT(w_dcdA[3:0] == iword[30:27]); |
`ASSERT(w_dcdB[3:0] == iword[17:14]); |
|
`ASSERT(w_cis_op == w_op); |
|
`ASSERT(w_cond[3] == (iword[21:19] == 3'b000)); |
`ASSERT(w_cond[2:0] == iword[21:19]); |
`ASSERT(!w_wF); |
end else begin |
`ASSERT(!w_brev); |
`ASSERT(!w_ldilo); |
end |
|
// |
// Multiply instructions |
always @(*) |
if ((!iword[`CISBIT])&&((w_cis_op == 5'ha) |
||(w_cis_op == 5'h0b) |
||(w_cis_op == 5'h0c))) |
begin |
`ASSERT(w_mpy); |
`ASSERT(!w_div); |
`ASSERT(!w_cmptst); |
`ASSERT(!w_mem); |
`ASSERT(!w_sto); |
`ASSERT(!w_ldi); |
`ASSERT(!w_mov); |
`ASSERT(!w_brev); |
`ASSERT(!w_ldilo); |
`ASSERT(!w_special); |
`ASSERT(!w_fpu); |
`ASSERT((w_rA)&&(w_wR)&&(w_ALU)); |
`ASSERT(w_rB == iword[`IMMSEL]); |
`ASSERT(w_dcdA[4] == i_gie); |
`ASSERT(w_dcdB[4] == i_gie); |
`ASSERT(w_dcdA[3:0] == iword[30:27]); |
`ASSERT(w_dcdB[3:0] == iword[17:14]); |
|
`ASSERT(w_cis_op == w_op); |
|
`ASSERT(w_cond[3] == (iword[21:19] == 3'b000)); |
`ASSERT(w_cond[2:0] == iword[21:19]); |
`ASSERT((w_wF == w_cond[3])||(w_dcdA[3:1]==3'b111)); |
end else |
`ASSERT(!w_mpy); |
|
// |
// Move instruction |
always @(*) |
if ((!iword[`CISBIT])&&((w_cis_op == 5'hd))) |
begin |
`ASSERT(w_mov); |
`ASSERT(!w_div); |
`ASSERT(!w_mpy); |
`ASSERT(!w_cmptst); |
`ASSERT(!w_mem); |
`ASSERT(!w_sto); |
`ASSERT(!w_ldi); |
`ASSERT(!w_brev); |
`ASSERT(!w_ldilo); |
`ASSERT(!w_special); |
`ASSERT(!w_fpu); |
`ASSERT((!w_rA)&&(w_wR)&&(w_ALU)); |
`ASSERT(w_rB); |
`ASSERT(w_dcdA[4] == ((i_gie)||(iword[`IMMSEL]))); |
`ASSERT(w_dcdB[4] == ((i_gie)||(iword[13]))); |
`ASSERT(w_dcdA[3:0] == iword[30:27]); |
`ASSERT(w_dcdB[3:0] == iword[17:14]); |
|
`ASSERT(w_cis_op == w_op); |
|
`ASSERT(w_cond[3] == (iword[21:19] == 3'b000)); |
`ASSERT(w_cond[2:0] == iword[21:19]); |
`ASSERT(!w_wF); |
end else if ((iword[`CISBIT])&&(iword[26:24]==3'b111)) |
begin |
`ASSERT(w_mov); |
`ASSERT(!w_div); |
`ASSERT(!w_mpy); |
`ASSERT(!w_cmptst); |
`ASSERT(!w_mem); |
`ASSERT(!w_sto); |
`ASSERT(!w_ldi); |
`ASSERT(!w_brev); |
`ASSERT(!w_ldilo); |
`ASSERT(!w_special); |
`ASSERT(!w_fpu); |
`ASSERT((!w_rA)&&(w_wR)&&(w_ALU)); |
`ASSERT(w_rB); |
`ASSERT(w_dcdA[4] == (i_gie)); |
`ASSERT(w_dcdB[4] == (i_gie)); |
`ASSERT(w_dcdA[3:0] == iword[30:27]); |
`ASSERT(w_dcdB[3:0] == iword[22:19]); |
|
`ASSERT(w_cis_op == 5'h0d); |
|
`ASSERT(w_cond == 4'h8); |
`ASSERT(!w_wF); |
end else |
`ASSERT(!w_mov); |
|
// |
// Divide instruction |
always @(*) |
if ((!iword[`CISBIT])&&(iword[26:23]==4'b0111)) |
begin |
`ASSERT(w_div); |
`ASSERT(!w_cmptst); |
`ASSERT(!w_mem); |
`ASSERT(!w_sto); |
`ASSERT(!w_ldi); |
`ASSERT(!w_mov); |
`ASSERT(!w_brev); |
`ASSERT(!w_ldilo); |
`ASSERT(!w_special); |
`ASSERT(!w_fpu); |
`ASSERT(!w_mpy); |
`ASSERT((w_rA)&&(w_wR)); |
`ASSERT(w_rB == iword[`IMMSEL]); |
`ASSERT(w_dcdA[4] == i_gie); |
`ASSERT(w_dcdB[4] == i_gie); |
`ASSERT(w_dcdA[3:0] == iword[30:27]); |
`ASSERT(w_dcdB[3:0] == iword[17:14]); |
|
`ASSERT(w_cis_op == w_op); |
|
`ASSERT(w_cond[3] == (iword[21:19] == 3'b000)); |
`ASSERT(w_cond[2:0] == iword[21:19]); |
`ASSERT((w_wF == w_cond[3])||(w_dcdA[3:1]==3'b111)); |
end else |
`ASSERT(!w_div); |
|
// |
// Comparison instructions |
always @(*) |
if ((!iword[`CISBIT])&&(iword[26:23]==4'b1000)) |
begin |
`ASSERT(w_cmptst); |
`ASSERT(!w_div); |
`ASSERT(!w_mem); |
`ASSERT(!w_sto); |
`ASSERT(!w_ldi); |
`ASSERT(!w_mov); |
`ASSERT(!w_brev); |
`ASSERT(!w_ldilo); |
`ASSERT(!w_special); |
`ASSERT(!w_fpu); |
`ASSERT(!w_mpy); |
`ASSERT((w_rA)&&(!w_wR)&&(!w_ALU)); |
`ASSERT(w_rB == iword[`IMMSEL]); |
`ASSERT(w_dcdA[4] == i_gie); |
`ASSERT(w_dcdB[4] == i_gie); |
`ASSERT(w_dcdA[3:0] == iword[30:27]); |
`ASSERT(w_dcdB[3:0] == iword[17:14]); |
|
`ASSERT(w_cis_op == w_op); |
|
`ASSERT(w_cond[3] == (iword[21:19] == 3'b000)); |
`ASSERT(w_cond[2:0] == iword[21:19]); |
`ASSERT(w_wF); |
end else if ((iword[`CISBIT])&&(iword[26:24]==3'b011)) |
begin |
`ASSERT(w_cmptst); |
`ASSERT(!w_div); |
`ASSERT(!w_mem); |
`ASSERT(!w_sto); |
`ASSERT(!w_ldi); |
`ASSERT(!w_mov); |
`ASSERT(!w_brev); |
`ASSERT(!w_ldilo); |
`ASSERT(!w_special); |
`ASSERT(!w_fpu); |
`ASSERT(!w_mpy); |
`ASSERT((w_rA)&&(!w_wR)&&(!w_ALU)); |
`ASSERT(w_rB == iword[`CISIMMSEL]); |
`ASSERT(w_dcdA[4] == i_gie); |
`ASSERT(w_dcdB[4] == i_gie); |
`ASSERT(w_dcdA[3:0] == iword[30:27]); |
`ASSERT(w_dcdB[3:0] == iword[22:19]); |
|
`ASSERT(w_cis_op == 5'h10); |
|
`ASSERT(w_cond == 4'h8); |
if (iword[`CISIMMSEL]) |
`ASSERT(w_I == { {(23-3){iword[18]}}, iword[18:16] }); |
else |
`ASSERT(w_I == { {(23-7){iword[22]}}, iword[22:16] }); |
`ASSERT(w_wF); |
end else |
`ASSERT(!w_cmptst); |
|
always @(posedge i_clk) |
if ((f_new_insn)&&($past(w_cmptst))) |
`ASSERT(o_ALU); |
|
// |
// Memory instructions |
always @(*) |
if ((!iword[`CISBIT])&&( |
(iword[26:23]==4'b1001) // Word |
||(iword[26:23]==4'b1010) // Half-word, or short |
||(iword[26:23]==4'b1011))) // Byte ops |
begin |
`ASSERT(w_mem); |
`ASSERT(w_sto == iword[22]); |
`ASSERT(!w_cmptst); |
`ASSERT(!w_div); |
`ASSERT(!w_ldi); |
`ASSERT(!w_mov); |
`ASSERT(!w_brev); |
`ASSERT(!w_ldilo); |
`ASSERT(!w_special); |
`ASSERT(!w_fpu); |
`ASSERT(!w_mpy); |
if (w_sto) |
`ASSERT((w_rA)&&(!w_wR)); |
else |
`ASSERT((!w_rA)&&(w_wR)); |
`ASSERT(!w_ALU); |
`ASSERT(w_rB == iword[`IMMSEL]); |
`ASSERT(w_dcdA[4] == i_gie); |
`ASSERT(w_dcdB[4] == i_gie); |
`ASSERT(w_dcdA[3:0] == iword[30:27]); |
`ASSERT(w_dcdB[3:0] == iword[17:14]); |
|
`ASSERT(w_cis_op == w_op); |
|
`ASSERT(w_cond[3] == (iword[21:19] == 3'b000)); |
`ASSERT(w_cond[2:0] == iword[21:19]); |
`ASSERT(!w_wF); |
end else if ((iword[`CISBIT])&&(iword[26:25]==2'b10)) |
begin |
`ASSERT(w_mem); |
`ASSERT(w_sto == iword[24]); |
`ASSERT(!w_cmptst); |
`ASSERT(!w_div); |
`ASSERT(!w_ldi); |
`ASSERT(!w_mov); |
`ASSERT(!w_brev); |
`ASSERT(!w_ldilo); |
`ASSERT(!w_special); |
`ASSERT(!w_fpu); |
`ASSERT(!w_mpy); |
if (w_sto) |
`ASSERT((w_rA)&&(!w_wR)); |
else |
`ASSERT((!w_rA)&&(w_wR)); |
`ASSERT(!w_ALU); |
`ASSERT(w_rB); |
`ASSERT(w_dcdA[4] == i_gie); |
`ASSERT(w_dcdB[4] == i_gie); |
`ASSERT(w_dcdA[3:0] == iword[30:27]); |
if (iword[`CISIMMSEL]) |
`ASSERT(w_dcdB[3:0] == iword[22:19]); |
else |
`ASSERT(w_dcdB[3:0] == `CPU_SP_REG); |
|
if (w_sto) |
`ASSERT(w_cis_op == 5'h13); |
else |
`ASSERT(w_cis_op == 5'h12); |
|
`ASSERT(w_cond == 4'h8); |
`ASSERT(!w_wF); |
end else begin |
`ASSERT(!w_sto); |
`ASSERT(!w_mem); |
end |
|
always @(*) |
if (w_sto) |
`ASSERT(w_mem); |
|
// |
// LDI -- Load immediate |
always @(*) |
if ((!iword[`CISBIT])&&(w_op[4:1] == 4'hc)) |
begin |
`ASSERT(w_ldi); |
`ASSERT(!w_mpy); |
`ASSERT(!w_div); |
`ASSERT(!w_cmptst); |
`ASSERT(!w_mem); |
`ASSERT(!w_sto); |
`ASSERT(!w_mov); |
`ASSERT(!w_brev); |
`ASSERT(!w_ldilo); |
`ASSERT((!w_rA)&&(w_wR)&&(!w_ALU)); |
`ASSERT(!w_special); |
`ASSERT(!w_fpu); |
`ASSERT(w_rB == 1'b0); |
`ASSERT(w_dcdA[4] == i_gie); |
`ASSERT(w_dcdB[4] == i_gie); |
`ASSERT(w_dcdA[3:0] == iword[30:27]); |
`ASSERT(w_dcdB[3:0] == iword[17:14]); |
|
`ASSERT(w_cis_op == w_op); |
|
`ASSERT(w_cond == 4'h8); |
`ASSERT(!w_wF); |
|
`ASSERT(w_Iz == (iword[22:0] == 0)); |
`ASSERT(w_I[22:0] == iword[22:0]); |
end else if ((iword[`CISBIT])&&(iword[26:24] == 3'b110)) |
begin |
`ASSERT(w_ldi); |
`ASSERT(!w_mpy); |
`ASSERT(!w_div); |
`ASSERT(!w_cmptst); |
`ASSERT(!w_mem); |
`ASSERT(!w_sto); |
`ASSERT(!w_mov); |
`ASSERT(!w_brev); |
`ASSERT(!w_ldilo); |
`ASSERT((!w_rA)&&(w_wR)&&(!w_ALU)); |
`ASSERT(!w_special); |
`ASSERT(!w_fpu); |
`ASSERT(w_rB == 1'b0); |
`ASSERT(w_dcdA[4] == i_gie); |
`ASSERT(w_dcdA[3:0] == iword[30:27]); |
|
`ASSERT(w_cis_op[4:1] == 4'hc); |
|
`ASSERT(w_cond == 4'h8); |
`ASSERT(!w_wF); |
|
`ASSERT(w_Iz == (iword[23:16] == 0)); |
`ASSERT(w_I[22:0] == { {(23-8){iword[23]}}, iword[23:16] }); |
end else |
`ASSERT(!w_ldi); |
`endif // IDECODE |
|
always @(posedge i_clk) |
if ((f_new_insn)&&($past(w_ldi))) |
`ASSERT(o_ALU); |
|
`ifdef IDECODE |
always @(*) |
if ((w_break)||(w_lock)||(w_sim)||(w_noop)) |
`ASSERT(w_special); |
|
|
// |
// FPU -- Floating point instructions |
always @(*) |
if ((!iword[`CISBIT])&&(OPT_FPU)&&( |
(w_cis_op[4:1] == 4'hd) |
||(w_cis_op[4:1] == 4'he) |
||(w_cis_op[4:1] == 4'hf)) |
&&(iword[30:28] != 3'h7)) |
begin |
`ASSERT(w_fpu); |
`ASSERT(!w_ldi); |
`ASSERT(!w_mpy); |
`ASSERT(!w_div); |
`ASSERT(!w_cmptst); |
`ASSERT(!w_mem); |
`ASSERT(!w_sto); |
`ASSERT(!w_mov); |
`ASSERT(!w_brev); |
`ASSERT(!w_ldilo); |
`ASSERT((w_wR)&&(!w_ALU)); |
if ((w_cis_op == 5'he)||(w_cis_op == 5'hf)) |
`ASSERT(!w_rA); |
else |
`ASSERT(w_rA); |
`ASSERT(!w_special); |
`ASSERT(w_rB == iword[`IMMSEL]); |
`ASSERT(w_dcdA[4] == i_gie); |
`ASSERT(w_dcdB[4] == i_gie); |
`ASSERT(w_dcdA[3:0] == iword[30:27]); |
`ASSERT(w_dcdB[3:0] == iword[17:14]); |
|
`ASSERT(w_cis_op == w_op); |
|
`ASSERT(w_cond[3] == (iword[21:19] == 3'b000)); |
`ASSERT(w_cond[2:0] == iword[21:19]); |
`ASSERT((w_wF == w_cond[3])||(w_dcdA[3:1]==3'b111)); |
end else |
`ASSERT((!w_fpu)||(!OPT_FPU)); |
|
// |
// Special instructions |
always @(*) |
if ((!iword[`CISBIT])&&( |
(w_cis_op == 5'h1c) |
||(w_cis_op == 5'h1d) |
||(w_cis_op == 5'h1e) |
||(w_cis_op == 5'h1f)) |
&&((iword[30:28] == 3'h7)||(!OPT_FPU))) |
begin |
`ASSERT(w_special); |
if (w_cis_op == 5'h1c) |
begin |
`ASSERT(w_break); |
`ASSERT(!w_lock); |
`ASSERT(!w_sim); |
`ASSERT(!w_noop); |
end else if (w_cis_op == 5'h1d) |
begin |
`ASSERT(!w_break); |
`ASSERT( w_lock); |
`ASSERT(!w_sim); |
`ASSERT(!w_noop); |
end else if (w_cis_op == 5'h1e) |
begin |
`ASSERT(!w_break); |
`ASSERT(!w_lock); |
`ASSERT( w_sim); |
`ASSERT(!w_noop); |
end else begin |
`ASSERT(!w_break); |
`ASSERT(!w_lock); |
`ASSERT(!w_sim); |
`ASSERT( w_noop); |
end |
`ASSERT((!w_fpu)||(!OPT_FPU)); |
`ASSERT(!w_ldi); |
`ASSERT(!w_mpy); |
`ASSERT(!w_div); |
`ASSERT(!w_cmptst); |
`ASSERT(!w_mem); |
`ASSERT(!w_sto); |
`ASSERT(!w_mov); |
`ASSERT(!w_brev); |
`ASSERT(!w_ldilo); |
|
`ASSERT((!w_rA)&&(!w_rB)&&(!w_wR)&&(!w_ALU)); |
|
`ASSERT(w_cis_op == w_op); |
|
`ASSERT(w_cond == 4'h8); |
`ASSERT(!w_wF); |
end else begin |
`ASSERT(!w_special); |
`ASSERT(!w_break); |
`ASSERT(!w_lock); |
`ASSERT(!w_sim); |
`ASSERT(!w_noop); |
end |
`endif |
|
generate if (OPT_EARLY_BRANCHING) |
begin |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_ce))&&(!$past(i_reset))&&(!i_reset)) |
begin |
if ($past(pf_valid)) |
begin |
if ($past(o_ljmp)) |
begin |
// 2nd half of LW (PC),PC |
`ASSERT(o_early_branch); |
`ASSERT(o_early_branch_stb); |
end else if ((!$past(iword[`CISBIT]))&&($past(w_add)) |
&&(!$past(w_rB)) |
&&($past(w_cond[3])) |
&&(o_dcdR[4:0]=={ i_gie, 4'hf })) |
begin |
// ADD #x,PC |
`ASSERT(o_early_branch); |
`ASSERT(o_early_branch_stb); |
end else if ((!$past(iword[`CISBIT])) |
&&($past(w_cis_op == 5'h12)) |
&&($past(w_rB)) |
&&($past(w_cond[3])) |
&&(o_zI) |
&&(o_dcdB[4:0]=={ i_gie, 4'hf }) |
&&(o_dcdR[4:0]=={ i_gie, 4'hf })) |
begin |
// LW (PC),PC |
`ASSERT(!o_early_branch); |
`ASSERT(!o_early_branch_stb); |
end else if ((OPT_CIS)&&($past(o_phase)) |
&&($past(w_cis_op == 5'h12)) |
&&($past(w_rB)) |
&&($past(w_cond[3])) |
&&($past(w_Iz)) |
&&($past(w_dcdB_pc)) |
&&($past(w_dcdR_pc)) |
&&(o_dcdR[4:0]=={ i_gie, 4'hf })) |
begin |
// (CIS) LW (PC),PC |
`ASSERT(!o_early_branch); |
`ASSERT(!o_early_branch_stb); |
end else begin |
`ASSERT(!o_early_branch); |
end |
end else if ((OPT_CIS)&&($past(o_phase))) |
begin |
if (($past(w_cis_op == 5'h12)) |
&&($past(w_rB)) |
&&($past(w_cond[3])) |
&&($past(w_Iz)) |
&&($past(w_dcdB_pc)) |
&&($past(w_dcdR_pc))) |
begin |
// (CIS) LW (PC),PC |
`ASSERT(!o_early_branch); |
`ASSERT(!o_early_branch_stb); |
end else begin |
`ASSERT(!o_early_branch); |
`ASSERT(!o_early_branch_stb); |
end |
end |
end else if (!i_reset) |
`ASSERT(!o_early_branch_stb); |
|
// // CIS instruction 16'hfcf8 decodes into: |
// // 1.1111.100.1.1111.0000 |
// // = LW (PC),PC |
// always @(*) |
// assume(i_instruction[31:16] != 16'hfcf8); |
|
end else begin |
always @(*) |
`ASSERT(!o_early_branch_stb); |
always @(*) |
`ASSERT(!o_early_branch); |
end endgenerate |
|
always @(*) |
if (o_early_branch_stb) |
`ASSERT(o_early_branch); |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_early_branch_stb))&&(!$past(pf_valid))) |
`ASSERT(!o_early_branch_stb); |
|
always @(*) |
if (!OPT_LOCK) |
`ASSERT(!o_lock); |
|
generate if (OPT_CIS) |
begin : F_OPT_CIS |
always @(*) |
if ((OPT_PIPELINED)&&(!o_valid)) |
`ASSERT(!o_phase); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))) |
begin |
if ((o_phase)&&($past(i_ce))) |
`ASSERT((iword[30:16] == $past(i_instruction[14:0])) |
&&(iword[`CISBIT])); |
else if (!o_phase) |
`ASSERT(iword == i_instruction); |
|
if ((!$past(o_phase))&&($past(i_ce)) |
&&($past(pf_valid)) |
&&(!$past(i_illegal)) |
&&(!$past(w_ljmp_dly)) |
&&($past(i_instruction[`CISBIT])) |
&&((!$past(w_dcdR_pc)) |
||(!$past(w_wR)))) |
`ASSERT(o_phase); |
else if (($past(o_phase))&&($past(i_ce))) |
`ASSERT(!o_phase); |
if (($past(i_ce))&&(!$past(o_phase)) |
&&($past(i_illegal))&&($past(i_pf_valid))) |
`ASSERT((o_illegal)&&(!o_phase)); |
|
`ASSERT((!o_phase)||(!o_ljmp)); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_stalled))&&($past(pf_valid)) |
&&($past(i_ce))) |
begin |
`ASSERT(o_pc[0] == 1'b0); |
if (!$past(iword[`CISBIT])) |
begin |
`ASSERT(o_pc[1:0]==2'b00); |
`ASSERT(o_pc[AW+1:2] == $past(i_pc[AW+1:2])+1'b1); |
end else if ($past(iword[`CISBIT])&&($past(o_phase))) |
`ASSERT(o_pc[(AW+1):1] == $past(o_pc[(AW+1):1]) + 1'b1); |
else if ($past(iword[`CISBIT])) |
begin |
`ASSERT(o_pc[(AW+1):1] == { $past(i_pc[(AW+1):2]), 1'b1}); |
if (o_valid) |
begin |
`ASSERT(o_pc[1]); |
`ASSERT((o_illegal)||(o_phase)); |
end |
end |
end |
|
|
always @(*) |
if (iword[`CISBIT]) |
begin |
`ASSERT((!w_ldi)||(w_I == { {(23-8){iword[23]}}, iword[23:16] })); |
`ASSERT((w_ldi)||(iword[`CISIMMSEL]) |
||(w_I == { {(23-7){iword[22]}}, iword[22:16] })); |
`ASSERT((w_ldi)||(!iword[`CISIMMSEL]) |
||(w_I == { {(23-3){iword[18]}}, iword[18:16] })); |
end else begin |
`ASSERT((!w_ldi)||(w_I == iword[22:0])); |
`ASSERT((!w_mov)||(w_I == { {(23-13){iword[12]}}, iword[12:0] })); |
`ASSERT((w_ldi)||(w_mov)||(iword[`IMMSEL]) |
||(w_I == { {(23-18){iword[17]}}, iword[17:0] })); |
`ASSERT((w_ldi)||(w_mov)||(!iword[`IMMSEL]) |
||(w_I == { {(23-14){iword[13]}}, iword[13:0] })); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(o_phase)&&($past(i_ce))) |
`ASSERT(($past(i_instruction[`CISBIT])) |
&&(r_nxt_half[14:0]==$past(i_instruction[14:0]))); |
end else begin |
|
always @(*) |
begin |
`ASSERT((o_phase)||(iword[30:0] == i_instruction[30:0])); |
`ASSERT(o_phase == 1'b0); |
`ASSERT(o_pc[0] == 1'b0); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_ce))&&($past(i_pf_valid))) |
`ASSERT(o_pc[AW+1:2] == $past(i_pc[AW+1:2]) + 1'b1); |
else if (f_past_valid) |
`ASSERT(o_pc == $past(o_pc)); |
|
always @(*) |
`ASSERT(o_pc[1:0] == 2'b00); |
|
always @(*) |
`ASSERT((!w_ldi)||(w_I == iword[22:0])); |
always @(*) |
`ASSERT((!w_mov)||(w_I == { {(23-13){iword[12]}}, iword[12:0] })); |
always @(*) |
`ASSERT((w_ldi)||(w_mov)||(iword[`IMMSEL]) |
||(w_I == { {(23-18){iword[17]}}, iword[17:0] })); |
always @(*) |
`ASSERT((w_ldi)||(w_mov)||(!iword[`IMMSEL]) |
||(w_I == { {(23-14){iword[13]}}, iword[13:0] })); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_ce))&&(!$past(i_reset))) |
`ASSERT((!$past(i_instruction[`CISBIT])) |
||(!$past(pf_valid))||(o_illegal)); |
end endgenerate |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_ce))&&($past(w_fpu))) |
begin |
if (OPT_FPU) |
`ASSERT(o_FP); |
else if (!$past(w_special)) |
`ASSERT(o_illegal); |
end |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_ce))&&($past(w_lock))) |
begin |
if (OPT_LOCK) |
`ASSERT(o_lock); |
else |
`ASSERT(o_illegal); |
end |
|
wire [20:0] f_next_pipe_I, f_this_pipe_I; |
assign f_this_pipe_I = r_I[22:2]; |
assign f_next_pipe_I = r_I[22:2]+1'b1; |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))) |
begin |
if (OPT_OPIPE) |
begin |
if (($past(i_ce)) |
&&(($past(pf_valid))||($past(o_phase)))) |
begin |
if ((!$past(o_M))||(!o_M)) |
`ASSERT(!o_pipe); |
else if ($past(o_op[0])!=o_op[0]) |
`ASSERT(!o_pipe); |
else if ($past(o_rB)!=o_rB) |
`ASSERT(!o_pipe); |
else if ((o_rB)&&($past(o_dcdB) != o_dcdB)) |
`ASSERT(!o_pipe); |
else if (($past(o_wR)) |
&&($past(o_dcdR[3:1]) == 3'h7)) |
`ASSERT(!o_pipe); |
// else if ((o_wR)&&(o_dcdR[3:1] == 3'h7)) |
// `ASSERT(!o_pipe); |
else if (o_wR != $past(o_wR)) |
`ASSERT(!o_pipe); |
else if ((o_wR)&&($past(o_dcdR) == o_dcdB)) |
`ASSERT(!o_pipe); |
else if ((o_wR)&&(o_dcdB[3:1] == 3'h7)) |
`ASSERT(!o_pipe); |
else if (($past(o_cond) != 4'h8) |
&&($past(o_cond) != o_cond)) |
`ASSERT(!o_pipe); |
else if ($past(r_I[22])!=r_I[22]) |
`ASSERT(!o_pipe); |
else if (r_I[22:0] - $past(r_I[22:0])>23'h4) |
`ASSERT(!o_pipe); |
else if (!$past(o_valid)) |
`ASSERT(!o_pipe); |
// else |
// assert(o_pipe); |
end else if ($past(i_stalled)) |
`ASSERT(o_pipe == $past(o_pipe)); |
end |
end |
|
always @(*) |
`ASSERT((OPT_OPIPE)||(!o_pipe)); |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_ce)) |
&&($past(i_pf_valid))&&($past(w_mpy))) |
`ASSERT((OPT_MPY)||(o_illegal)); |
|
always @(*) |
if (o_valid) |
`ASSERT((!o_phase)||(!o_early_branch)); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_valid))&&($past(o_ljmp))&&($past(!i_stalled))) |
`ASSERT(!o_valid); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_early_branch_stb))) |
begin |
`ASSERT(!o_phase); |
if (!$past(i_stalled)) |
`ASSERT(!o_valid); |
`ASSERT(!o_ljmp); |
end |
|
// Unless another valid instruction comes along, once o_ljmp is asserted |
// it should stay asserted until either a reset or an early branch |
// strobe. |
always @(posedge i_clk) |
if ((OPT_EARLY_BRANCHING)&&(f_past_valid) |
&&($past(o_ljmp))&&(!$past(pf_valid)) |
&&(!$past(i_reset))&&(!$past(o_early_branch_stb))) |
`ASSERT(o_ljmp); |
|
// o_ljmp should only ever be asserted following a valid prefetch |
// input. Hence, if the prefetch input isn't valid, then o_ljmp |
// should be left low |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(o_ljmp)) |
&&( (!$past(pf_valid)) || (!$past(i_ce)) ) |
&&( !$past(o_phase) ) |
&&(!$past(i_reset))&&(!$past(o_early_branch_stb))) |
`ASSERT(!o_ljmp); |
|
always @(posedge i_clk) |
if ((OPT_EARLY_BRANCHING)&&(f_past_valid)&&($past(o_ljmp))&&(!o_ljmp) |
&&(!$past(i_reset))) |
`ASSERT((o_early_branch_stb)&&(!o_valid)); |
|
always @(posedge i_clk) |
`ASSERT((!o_early_branch_stb)||(!o_ljmp)); |
|
always @(posedge i_clk) |
`ASSERT((!o_valid)||(!o_ljmp)||(o_phase == o_pc[1])); |
|
always @(posedge i_clk) |
if (!OPT_CIS) |
`ASSERT(!o_phase); |
else if (!f_insn_word[31]) |
`ASSERT(!o_phase); |
else if (o_phase) |
`ASSERT(o_pc[1]); |
|
always @(*) |
if ((o_early_branch)&&(!o_early_branch_stb)) |
`ASSERT(!o_pipe); |
|
always @(*) |
if (o_ljmp) |
`ASSERT(!o_pipe); |
|
always @(*) |
`ASSERT(o_dcdR == o_dcdA); |
|
always @(*) |
if ((o_valid)&&(o_phase)) |
begin |
`ASSERT(!o_illegal); |
`ASSERT(o_pc[1]); |
`ASSERT(f_insn_word[31]); |
end |
|
always @(*) |
`ASSERT(o_branch_pc[1:0] == 2'b00); |
always @(*) |
`ASSERT(o_pc[0] == 1'b0); |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_pf_valid))&&(i_pf_valid)) |
`ASSUME((i_reset)||($stable(i_gie))); |
|
wire fc_illegal, fc_wF, fc_ALU, fc_M, fc_DV, fc_FP, fc_break, |
fc_lock, fc_wR, fc_rA, fc_rB, fc_prepipe, fc_sim; |
wire [6:0] fc_dcdR, fc_dcdA, fc_dcdB; |
wire [31:0] fc_I; |
wire [3:0] fc_cond; |
wire [3:0] fc_op; |
wire [22:0] fc_sim_immv; |
f_idecode #( |
.ADDRESS_WIDTH(AW), |
.OPT_MPY(OPT_MPY), |
.OPT_EARLY_BRANCHING(OPT_EARLY_BRANCHING), |
.OPT_DIVIDE(OPT_DIVIDE), |
.OPT_FPU(OPT_FPU), |
.OPT_CIS(OPT_CIS), |
.OPT_LOCK(OPT_LOCK), |
.OPT_OPIPE(OPT_OPIPE), |
.OPT_SIM(OPT_SIM) |
) formal_decoder( |
f_insn_word, o_phase, f_insn_gie, |
fc_illegal, |
fc_dcdR, fc_dcdA,fc_dcdB, fc_I, fc_cond, fc_wF, fc_op, |
fc_ALU, fc_M, fc_DV, fc_FP, fc_break, fc_lock, |
fc_wR, fc_rA, fc_rB, fc_prepipe, fc_sim, fc_sim_immv); |
|
always @(posedge i_clk) |
if ((o_valid)&&(fc_illegal)) |
assert(o_illegal); |
|
always @(posedge i_clk) |
if ((o_valid)&&(!o_illegal)) |
begin |
`ASSERT(fc_dcdR== o_dcdR); // |
`ASSERT(fc_dcdA== o_dcdA); // |
`ASSERT(fc_dcdB== o_dcdB); // |
`ASSERT(fc_I == o_I); |
`ASSERT(o_zI == (fc_I == 0)); |
`ASSERT(fc_cond== o_cond); |
`ASSERT(fc_wF == o_wF); |
`ASSERT(fc_op == o_op); |
`ASSERT(fc_ALU == o_ALU); |
`ASSERT(fc_M == o_M); |
`ASSERT(fc_DV == o_DV); |
`ASSERT(fc_FP == o_FP); |
`ASSERT(fc_break== o_break); |
`ASSERT(fc_lock == o_lock); |
`ASSERT(fc_wR == o_wR); |
`ASSERT(fc_rA == o_rA); |
`ASSERT(fc_rB == o_rB); |
`ASSERT(fc_sim == o_sim); |
`ASSERT(fc_sim_immv == o_sim_immv); |
`ASSERT(fc_prepipe == r_insn_is_pipeable); |
end else |
`ASSERT(!r_insn_is_pipeable); |
|
always @(*) |
if (o_phase) |
`ASSERT(r_nxt_half[14:0] == f_insn_word[14:0]); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_ce))&&(o_valid)&&(!$past(i_reset))) |
begin |
`ASSERT(((fc_illegal) |
||$past((i_illegal)&&(!o_phase)) |
||$past((o_illegal)&&( o_phase)))== o_illegal); |
end |
|
always @(posedge i_clk) |
if ((!o_valid)||(o_illegal)) |
`ASSERT(!r_insn_is_pipeable); |
|
generate if ((OPT_CIS)&&(OPT_EARLY_BRANCHING)) |
begin |
|
always @(*) |
if ((o_valid) |
// LW |
&&(o_M)&&(o_op[2:0]==3'b010) |
// Zero immediate |
&&(o_zI) |
// Unconditional |
&&(o_cond[3]) |
// From PC to PC |
&&(o_dcdR[5])&&(o_dcdB[5])) |
`ASSERT((o_ljmp) |
||((f_insn_word[31])&&(o_phase || o_illegal))); |
else if (o_valid) |
`ASSERT(!o_ljmp); |
|
end endgenerate |
|
`endif // FORMAL |
endmodule |
/core/memops.v
6,7 → 6,7
// |
// Purpose: A memory unit to support a CPU. |
// |
// In the interests of code simplicity, this memory operator is |
// In the interests of code simplicity, this memory operator is |
// susceptible to unknown results should a new command be sent to it |
// before it completes the last one. Unpredictable results might then |
// occurr. |
19,7 → 19,7
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015,2017-2019, Gisselquist Technology, LLC |
// Copyright (C) 2015,2017, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
43,32 → 43,22
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
// |
module memops(i_clk, i_reset, i_stb, i_lock, |
module memops(i_clk, i_rst, i_stb, i_lock, |
i_op, i_addr, i_data, i_oreg, |
o_busy, o_valid, o_err, o_wreg, o_result, |
o_wb_cyc_gbl, o_wb_cyc_lcl, |
o_wb_stb_gbl, o_wb_stb_lcl, |
o_wb_we, o_wb_addr, o_wb_data, o_wb_sel, |
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data |
`ifdef FORMAL |
, f_nreqs, f_nacks, f_outstanding |
`endif |
); |
parameter ADDRESS_WIDTH=30; |
parameter [0:0] IMPLEMENT_LOCK=1'b1, |
WITH_LOCAL_BUS=1'b1, |
OPT_ALIGNMENT_ERR=1'b1, |
OPT_ZERO_ON_IDLE=1'b0; |
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data); |
parameter ADDRESS_WIDTH=30, IMPLEMENT_LOCK=0, WITH_LOCAL_BUS=0; |
localparam AW=ADDRESS_WIDTH; |
input wire i_clk, i_reset; |
input wire i_stb, i_lock; |
input i_clk, i_rst; |
input i_stb, i_lock; |
// CPU interface |
input wire [2:0] i_op; |
input wire [31:0] i_addr; |
input wire [31:0] i_data; |
input wire [4:0] i_oreg; |
input [2:0] i_op; |
input [31:0] i_addr; |
input [31:0] i_data; |
input [4:0] i_oreg; |
// CPU outputs |
output wire o_busy; |
output reg o_valid; |
85,91 → 75,51
output reg [31:0] o_wb_data; |
output reg [3:0] o_wb_sel; |
// Wishbone inputs |
input wire i_wb_ack, i_wb_stall, i_wb_err; |
input wire [31:0] i_wb_data; |
// Formal |
parameter F_LGDEPTH = 2; |
`ifdef FORMAL |
output wire [(F_LGDEPTH-1):0] f_nreqs, f_nacks, f_outstanding; |
`endif |
input i_wb_ack, i_wb_stall, i_wb_err; |
input [31:0] i_wb_data; |
|
reg misaligned; |
|
generate if (OPT_ALIGNMENT_ERR) |
begin : GENERATE_ALIGNMENT_ERR |
always @(*) |
casez({ i_op[2:1], i_addr[1:0] }) |
4'b01?1: misaligned = i_stb; // Words must be halfword aligned |
4'b0110: misaligned = i_stb; // Words must be word aligned |
4'b10?1: misaligned = i_stb; // Halfwords must be aligned |
// 4'b11??: misaligned <= 1'b0; Byte access are never misaligned |
default: misaligned = 1'b0; |
endcase |
end else |
always @(*) misaligned = 1'b0; |
endgenerate |
|
reg r_wb_cyc_gbl, r_wb_cyc_lcl; |
wire gbl_stb, lcl_stb; |
assign lcl_stb = (i_stb)&&(WITH_LOCAL_BUS!=0)&&(i_addr[31:24]==8'hff) |
&&(!misaligned); |
assign gbl_stb = (i_stb)&&((WITH_LOCAL_BUS==0)||(i_addr[31:24]!=8'hff)) |
&&(!misaligned); |
assign lcl_stb = (i_stb)&&(WITH_LOCAL_BUS!=0)&&(i_addr[31:24]==8'hff); |
assign gbl_stb = (i_stb)&&((WITH_LOCAL_BUS==0)||(i_addr[31:24]!=8'hff)); |
|
initial r_wb_cyc_gbl = 1'b0; |
initial r_wb_cyc_lcl = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
begin |
r_wb_cyc_gbl <= 1'b0; |
r_wb_cyc_lcl <= 1'b0; |
end else if ((r_wb_cyc_gbl)||(r_wb_cyc_lcl)) |
begin |
if ((i_wb_ack)||(i_wb_err)) |
if (i_rst) |
begin |
r_wb_cyc_gbl <= 1'b0; |
r_wb_cyc_lcl <= 1'b0; |
end else if ((r_wb_cyc_gbl)||(r_wb_cyc_lcl)) |
begin |
if ((i_wb_ack)||(i_wb_err)) |
begin |
r_wb_cyc_gbl <= 1'b0; |
r_wb_cyc_lcl <= 1'b0; |
end |
end else if (i_stb) // New memory operation |
begin // Grab the wishbone |
r_wb_cyc_lcl <= lcl_stb; |
r_wb_cyc_gbl <= gbl_stb; |
end |
end else begin // New memory operation |
// Grab the wishbone |
r_wb_cyc_lcl <= (lcl_stb); |
r_wb_cyc_gbl <= (gbl_stb); |
end |
initial o_wb_stb_gbl = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
o_wb_stb_gbl <= 1'b0; |
else if ((i_wb_err)&&(r_wb_cyc_gbl)) |
o_wb_stb_gbl <= 1'b0; |
else if (o_wb_cyc_gbl) |
o_wb_stb_gbl <= (o_wb_stb_gbl)&&(i_wb_stall); |
else |
// Grab wishbone on any new transaction to the gbl bus |
o_wb_stb_gbl <= (gbl_stb); |
|
initial o_wb_stb_lcl = 1'b0; |
if (o_wb_cyc_gbl) |
o_wb_stb_gbl <= (o_wb_stb_gbl)&&(i_wb_stall); |
else |
o_wb_stb_gbl <= gbl_stb; // Grab wishbone on new operation |
always @(posedge i_clk) |
if (i_reset) |
o_wb_stb_lcl <= 1'b0; |
else if ((i_wb_err)&&(r_wb_cyc_lcl)) |
o_wb_stb_lcl <= 1'b0; |
else if (o_wb_cyc_lcl) |
o_wb_stb_lcl <= (o_wb_stb_lcl)&&(i_wb_stall); |
else |
// Grab wishbone on any new transaction to the lcl bus |
o_wb_stb_lcl <= (lcl_stb); |
if (o_wb_cyc_lcl) |
o_wb_stb_lcl <= (o_wb_stb_lcl)&&(i_wb_stall); |
else |
o_wb_stb_lcl <= lcl_stb; // Grab wishbone on new operation |
|
reg [3:0] r_op; |
initial o_wb_we = 1'b0; |
initial o_wb_data = 0; |
initial o_wb_sel = 0; |
always @(posedge i_clk) |
if (i_stb) |
begin |
o_wb_we <= i_op[0]; |
if (OPT_ZERO_ON_IDLE) |
if (i_stb) |
begin |
o_wb_we <= i_op[0]; |
casez({ i_op[2:1], i_addr[1:0] }) |
`ifdef ZERO_ON_IDLE |
4'b100?: o_wb_data <= { i_data[15:0], 16'h00 }; |
4'b101?: o_wb_data <= { 16'h00, i_data[15:0] }; |
4'b1100: o_wb_data <= { i_data[7:0], 24'h00 }; |
176,62 → 126,58
4'b1101: o_wb_data <= { 8'h00, i_data[7:0], 16'h00 }; |
4'b1110: o_wb_data <= { 16'h00, i_data[7:0], 8'h00 }; |
4'b1111: o_wb_data <= { 24'h00, i_data[7:0] }; |
default: o_wb_data <= i_data; |
endcase |
end else |
casez({ i_op[2:1], i_addr[1:0] }) |
`else |
4'b10??: o_wb_data <= { (2){ i_data[15:0] } }; |
4'b11??: o_wb_data <= { (4){ i_data[7:0] } }; |
`endif |
default: o_wb_data <= i_data; |
endcase |
|
o_wb_addr <= i_addr[(AW+1):2]; |
casez({ i_op[2:1], i_addr[1:0] }) |
4'b01??: o_wb_sel <= 4'b1111; |
4'b100?: o_wb_sel <= 4'b1100; |
4'b101?: o_wb_sel <= 4'b0011; |
4'b1100: o_wb_sel <= 4'b1000; |
4'b1101: o_wb_sel <= 4'b0100; |
4'b1110: o_wb_sel <= 4'b0010; |
4'b1111: o_wb_sel <= 4'b0001; |
default: o_wb_sel <= 4'b1111; |
endcase |
r_op <= { i_op[2:1] , i_addr[1:0] }; |
end else if ((OPT_ZERO_ON_IDLE)&&(!o_wb_cyc_gbl)&&(!o_wb_cyc_lcl)) |
begin |
o_wb_we <= 1'b0; |
o_wb_addr <= 0; |
o_wb_data <= 32'h0; |
o_wb_sel <= 4'h0; |
end |
o_wb_addr <= i_addr[(AW+1):2]; |
`ifdef SET_SEL_ON_READ |
if (i_op[0] == 1'b0) |
o_wb_sel <= 4'hf; |
else |
`endif |
casez({ i_op[2:1], i_addr[1:0] }) |
4'b01??: o_wb_sel <= 4'b1111; |
4'b100?: o_wb_sel <= 4'b1100; |
4'b101?: o_wb_sel <= 4'b0011; |
4'b1100: o_wb_sel <= 4'b1000; |
4'b1101: o_wb_sel <= 4'b0100; |
4'b1110: o_wb_sel <= 4'b0010; |
4'b1111: o_wb_sel <= 4'b0001; |
default: o_wb_sel <= 4'b1111; |
endcase |
r_op <= { i_op[2:1] , i_addr[1:0] }; |
end |
`ifdef ZERO_ON_IDLE |
else if ((!o_wb_cyc_gbl)&&(!o_wb_cyc_lcl)) |
begin |
o_wb_we <= 1'b0; |
o_wb_addr <= 0; |
o_wb_data <= 32'h0; |
o_wb_sel <= 4'h0; |
end |
`endif |
|
initial o_valid = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
o_valid <= 1'b0; |
else |
o_valid <= (((o_wb_cyc_gbl)||(o_wb_cyc_lcl)) |
&&(i_wb_ack)&&(!o_wb_we)); |
o_valid <= (!i_rst)&&((o_wb_cyc_gbl)||(o_wb_cyc_lcl))&&(i_wb_ack)&&(~o_wb_we); |
initial o_err = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
o_err <= 1'b0; |
else if ((r_wb_cyc_gbl)||(r_wb_cyc_lcl)) |
o_err <= i_wb_err; |
else if ((i_stb)&&(!o_busy)) |
o_err <= misaligned; |
else |
o_err <= 1'b0; |
o_err <= (!i_rst)&&((o_wb_cyc_gbl)||(o_wb_cyc_lcl))&&(i_wb_err); |
assign o_busy = (o_wb_cyc_gbl)||(o_wb_cyc_lcl); |
|
assign o_busy = (r_wb_cyc_gbl)||(r_wb_cyc_lcl); |
|
always @(posedge i_clk) |
if (i_stb) |
o_wreg <= i_oreg; |
if (i_stb) |
o_wreg <= i_oreg; |
always @(posedge i_clk) |
if ((OPT_ZERO_ON_IDLE)&&(!i_wb_ack)) |
o_result <= 32'h0; |
else begin |
`ifdef ZERO_ON_IDLE |
if (!i_wb_ack) |
o_result <= 32'h0; |
else |
`endif |
casez(r_op) |
4'b01??: o_result <= i_wb_data; |
4'b100?: o_result <= { 16'h00, i_wb_data[31:16] }; |
242,438 → 188,25
4'b1111: o_result <= { 24'h00, i_wb_data[ 7: 0] }; |
default: o_result <= i_wb_data; |
endcase |
end |
|
reg lock_gbl, lock_lcl; |
|
generate |
if (IMPLEMENT_LOCK != 0) |
begin |
reg lock_gbl, lock_lcl; |
|
initial lock_gbl = 1'b0; |
initial lock_lcl = 1'b0; |
|
always @(posedge i_clk) |
if (i_reset) |
begin |
lock_gbl <= 1'b0; |
lock_lcl <= 1'b0; |
end else if (((i_wb_err)&&((r_wb_cyc_gbl)||(r_wb_cyc_lcl))) |
||(misaligned)) |
begin |
// Kill the lock if |
// there's a bus error, or |
// User requests a misaligned memory op |
lock_gbl <= 1'b0; |
lock_lcl <= 1'b0; |
end else begin |
// Kill the lock if |
// i_lock goes down |
// User starts on the global bus, then switches |
// to local or vice versa |
lock_gbl <= (i_lock)&&((r_wb_cyc_gbl)||(lock_gbl)) |
&&(!lcl_stb); |
lock_lcl <= (i_lock)&&((r_wb_cyc_lcl)||(lock_lcl)) |
&&(!gbl_stb); |
lock_gbl <= (i_lock)&&((r_wb_cyc_gbl)||(lock_gbl)); |
lock_lcl <= (i_lock)&&((r_wb_cyc_lcl)||(lock_lcl)); |
end |
|
assign o_wb_cyc_gbl = (r_wb_cyc_gbl)||(lock_gbl); |
assign o_wb_cyc_lcl = (r_wb_cyc_lcl)||(lock_lcl); |
end else begin |
|
assign o_wb_cyc_gbl = (r_wb_cyc_gbl); |
assign o_wb_cyc_lcl = (r_wb_cyc_lcl); |
|
always @(*) |
{ lock_gbl, lock_lcl } = 2'b00; |
|
// Make verilator happy |
// verilator lint_off UNUSED |
wire [2:0] lock_unused; |
assign lock_unused = { i_lock, lock_gbl, lock_lcl }; |
// verilator lint_on UNUSED |
|
end endgenerate |
|
`ifdef VERILATOR |
always @(posedge i_clk) |
if ((r_wb_cyc_gbl)||(r_wb_cyc_lcl)) |
assert(!i_stb); |
`endif |
|
|
// Make verilator happy |
// verilator lint_off UNUSED |
generate if (AW < 22) |
begin : TOO_MANY_ADDRESS_BITS |
|
wire [(21-AW):0] unused_addr; |
assign unused_addr = i_addr[23:(AW+2)]; |
|
end endgenerate |
// verilator lint_on UNUSED |
|
`ifdef FORMAL |
`define ASSERT assert |
`ifdef MEMOPS |
`define ASSUME assume |
`else |
`define ASSUME assert |
`endif |
|
reg f_past_valid; |
initial f_past_valid = 0; |
always @(posedge i_clk) |
f_past_valid = 1'b1; |
always @(*) |
if (!f_past_valid) |
`ASSUME(i_reset); |
initial `ASSUME(!i_stb); |
|
wire f_cyc, f_stb; |
assign f_cyc = (o_wb_cyc_gbl)||(o_wb_cyc_lcl); |
assign f_stb = (o_wb_stb_gbl)||(o_wb_stb_lcl); |
|
`ifdef MEMOPS |
`define MASTER fwb_master |
`else |
`define MASTER fwb_counter |
`endif |
|
fwb_master #(.AW(AW), .F_LGDEPTH(F_LGDEPTH), |
.F_OPT_RMW_BUS_OPTION(IMPLEMENT_LOCK), |
.F_OPT_DISCONTINUOUS(IMPLEMENT_LOCK)) |
f_wb(i_clk, i_reset, |
f_cyc, f_stb, o_wb_we, o_wb_addr, o_wb_data, o_wb_sel, |
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err, |
f_nreqs, f_nacks, f_outstanding); |
|
|
// Rule: Only one of the two CYC's may be valid, never both |
always @(posedge i_clk) |
`ASSERT((!o_wb_cyc_gbl)||(!o_wb_cyc_lcl)); |
|
// Rule: Only one of the two STB's may be valid, never both |
always @(posedge i_clk) |
`ASSERT((!o_wb_stb_gbl)||(!o_wb_stb_lcl)); |
|
// Rule: if WITH_LOCAL_BUS is ever false, neither the local STB nor CYC |
// may be valid |
always @(*) |
if (!WITH_LOCAL_BUS) |
begin |
`ASSERT(!o_wb_cyc_lcl); |
`ASSERT(!o_wb_stb_lcl); |
end |
|
// Rule: If the global CYC is ever true, the LCL one cannot be true |
// on the next clock without an intervening idle of both |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(r_wb_cyc_gbl))) |
`ASSERT(!r_wb_cyc_lcl); |
|
// Same for if the LCL CYC is true |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(r_wb_cyc_lcl))) |
`ASSERT(!r_wb_cyc_gbl); |
|
// STB can never be true unless CYC is also true |
always @(posedge i_clk) |
if (o_wb_stb_gbl) |
`ASSERT(r_wb_cyc_gbl); |
always @(posedge i_clk) |
if (o_wb_stb_lcl) |
`ASSERT(r_wb_cyc_lcl); |
|
// This core only ever has zero or one outstanding transaction(s) |
always @(posedge i_clk) |
if ((o_wb_stb_gbl)||(o_wb_stb_lcl)) |
`ASSERT(f_outstanding == 0); |
else |
`ASSERT((f_outstanding == 0)||(f_outstanding == 1)); |
|
// The LOCK function only allows up to two transactions (at most) |
// before CYC must be dropped. |
always @(posedge i_clk) |
if ((o_wb_stb_gbl)||(o_wb_stb_lcl)) |
begin |
if (IMPLEMENT_LOCK) |
`ASSERT((f_outstanding == 0)||(f_outstanding == 1)); |
else |
`ASSERT(f_nreqs <= 1); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(o_busy)) |
begin |
|
// If i_stb doesn't change, then neither do any of the other |
// inputs |
if (($past(i_stb))&&(i_stb)) |
begin |
`ASSUME($stable(i_op)); |
`ASSUME($stable(i_addr)); |
`ASSUME($stable(i_data)); |
`ASSUME($stable(i_oreg)); |
`ASSUME($stable(i_lock)); |
end |
|
|
// No strobe's are allowed if a request is outstanding, either |
// having been accepted by the bus or waiting to be accepted |
// by the bus. |
if ((f_outstanding != 0)||(f_stb)) |
`ASSUME(!i_stb); |
/* |
if (o_busy) |
assert( (!i_stb) |
||((!o_wb_stb_gbl)&&(!o_wb_stb_lcl)&&(i_lock))); |
|
if ((f_cyc)&&($past(f_cyc))) |
assert($stable(r_op)); |
*/ |
end |
|
always @(*) |
if (!IMPLEMENT_LOCK) |
`ASSUME(!i_lock); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(f_cyc))&&($past(!i_lock))) |
`ASSUME(!i_lock); |
|
// Following any i_stb request, assuming we are idle, immediately |
// begin a bus transaction |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_stb)) |
&&(!$past(f_cyc))&&(!$past(i_reset))) |
begin |
if ($past(misaligned)) |
begin |
`ASSERT(!f_cyc); |
`ASSERT(!o_busy); |
`ASSERT(o_err); |
`ASSERT(!o_valid); |
end else begin |
`ASSERT(f_cyc); |
`ASSERT(o_busy); |
end |
end |
|
always @(posedge i_clk) |
if (o_busy) |
`ASSUME(!i_stb); |
|
always @(posedge i_clk) |
if (o_wb_cyc_gbl) |
`ASSERT((o_busy)||(lock_gbl)); |
|
always @(posedge i_clk) |
if (o_wb_cyc_lcl) |
`ASSERT((o_busy)||(lock_lcl)); |
|
always @(posedge i_clk) |
if (f_outstanding > 0) |
`ASSERT(o_busy); |
|
// If a transaction ends in an error, send o_err on the output port. |
always @(posedge i_clk) |
if (f_past_valid) |
begin |
if ($past(i_reset)) |
`ASSERT(!o_err); |
else if (($past(f_cyc))&&($past(i_wb_err))) |
`ASSERT(o_err); |
else if ($past(misaligned)) |
`ASSERT(o_err); |
end |
|
// Always following a successful ACK, return an O_VALID value. |
always @(posedge i_clk) |
if (f_past_valid) |
begin |
if ($past(i_reset)) |
`ASSERT(!o_valid); |
else if(($past(f_cyc))&&($past(i_wb_ack)) |
&&(!$past(o_wb_we))) |
`ASSERT(o_valid); |
else if ($past(misaligned)) |
`ASSERT((!o_valid)&&(o_err)); |
else |
`ASSERT(!o_valid); |
end |
|
//always @(posedge i_clk) |
// if ((f_past_valid)&&($past(f_cyc))&&(!$past(o_wb_we))&&($past(i_wb_ack))) |
|
/* |
input wire [2:0] i_op; |
input wire [31:0] i_addr; |
input wire [31:0] i_data; |
input wire [4:0] i_oreg; |
// CPU outputs |
output wire o_busy; |
output reg o_valid; |
output reg o_err; |
output reg [4:0] o_wreg; |
output reg [31:0] o_result; |
*/ |
|
initial o_wb_we = 1'b0; |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_stb))) |
begin |
// On a write, assert o_wb_we should be true |
assert( $past(i_op[0]) == o_wb_we); |
|
// Word write |
if ($past(i_op[2:1]) == 2'b01) |
begin |
`ASSERT(o_wb_sel == 4'hf); |
`ASSERT(o_wb_data == $past(i_data)); |
end |
|
// Halfword (short) write |
if ($past(i_op[2:1]) == 2'b10) |
begin |
if (!$past(i_addr[1])) |
begin |
`ASSERT(o_wb_sel == 4'hc); |
`ASSERT(o_wb_data[31:16] == $past(i_data[15:0])); |
end else begin |
`ASSERT(o_wb_sel == 4'h3); |
`ASSERT(o_wb_data[15:0] == $past(i_data[15:0])); |
end |
end |
|
if ($past(i_op[2:1]) == 2'b11) |
begin |
if ($past(i_addr[1:0])==2'b00) |
begin |
`ASSERT(o_wb_sel == 4'h8); |
`ASSERT(o_wb_data[31:24] == $past(i_data[7:0])); |
end |
|
if ($past(i_addr[1:0])==2'b01) |
begin |
`ASSERT(o_wb_sel == 4'h4); |
`ASSERT(o_wb_data[23:16] == $past(i_data[7:0])); |
end |
if ($past(i_addr[1:0])==2'b10) |
begin |
`ASSERT(o_wb_sel == 4'h2); |
`ASSERT(o_wb_data[15:8] == $past(i_data[7:0])); |
end |
if ($past(i_addr[1:0])==2'b11) |
begin |
`ASSERT(o_wb_sel == 4'h1); |
`ASSERT(o_wb_data[7:0] == $past(i_data[7:0])); |
end |
end |
|
`ASSUME($past(i_op[2:1] != 2'b00)); |
end |
|
// This logic is fixed in the definitions of the lock(s) above |
// i.e., the user cna be stupid and this will still work |
/* |
always @(posedge i_clk) |
if ((i_lock)&&(i_stb)&&(WITH_LOCAL_BUS)) |
begin |
restrict((lock_gbl)||(i_addr[31:24] ==8'hff)); |
restrict((lock_lcl)||(i_addr[31:24]!==8'hff)); |
end |
*/ |
|
always @(posedge i_clk) |
if (o_wb_stb_lcl) |
`ASSERT(o_wb_addr[29:22] == 8'hff); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(misaligned))) |
begin |
`ASSERT(!o_wb_cyc_gbl); |
`ASSERT(!o_wb_cyc_lcl); |
`ASSERT(!o_wb_stb_gbl); |
`ASSERT(!o_wb_stb_lcl); |
`ASSERT(o_err); |
//OPT_ALIGNMENT_ERR=1'b0, |
//OPT_ZERO_ON_IDLE=1'b0; |
end |
|
always @(posedge i_clk) |
if ((!f_past_valid)||($past(i_reset))) |
`ASSUME(!i_stb); |
always @(*) |
if (o_busy) |
`ASSUME(!i_stb); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(IMPLEMENT_LOCK) |
&&(!$past(i_reset))&&(!$past(i_wb_err)) |
&&(!$past(misaligned)) |
&&(!$past(lcl_stb)) |
&&($past(i_lock))&&($past(lock_gbl))) |
assert(lock_gbl); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(IMPLEMENT_LOCK) |
&&(!$past(i_reset))&&(!$past(i_wb_err)) |
&&(!$past(misaligned)) |
&&(!$past(lcl_stb)) |
&&($past(o_wb_cyc_gbl))&&($past(i_lock)) |
&&($past(lock_gbl))) |
assert(o_wb_cyc_gbl); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(IMPLEMENT_LOCK) |
&&(!$past(i_reset))&&(!$past(i_wb_err)) |
&&(!$past(misaligned)) |
&&(!$past(gbl_stb)) |
&&($past(o_wb_cyc_lcl))&&($past(i_lock)) |
&&($past(lock_lcl))) |
assert(o_wb_cyc_lcl); |
|
// |
// Cover properties |
// |
always @(posedge i_clk) |
cover(i_wb_ack); |
|
// Cover a response on the same clock it is made |
always @(posedge i_clk) |
cover((o_wb_stb_gbl)&&(i_wb_ack)); |
|
// Cover a response a clock later |
always @(posedge i_clk) |
cover((o_wb_stb_gbl)&&(i_wb_ack)); |
|
|
generate if (WITH_LOCAL_BUS) |
begin |
|
// Same things on the local bus |
always @(posedge i_clk) |
cover((o_wb_cyc_lcl)&&(!o_wb_stb_lcl)&&(i_wb_ack)); |
always @(posedge i_clk) |
cover((o_wb_stb_lcl)&&(i_wb_ack)); |
|
end endgenerate |
|
`endif |
endmodule |
// |
// |
// Usage (from yosys): |
// (BFOR) (!ZOI,ALIGN) (ZOI,ALIGN) (!ZOI,!ALIGN) |
// Cells 230 226 281 225 |
// FDRE 114 116 116 116 |
// LUT2 17 23 76 19 |
// LUT3 9 23 17 20 |
// LUT4 15 4 11 14 |
// LUT5 18 18 7 15 |
// LUT6 33 18 54 38 |
// MUX7 16 12 2 |
// MUX8 8 1 1 |
// |
// |
/core/prefetch.v
6,31 → 6,25
// |
// Purpose: This is a very simple instruction fetch approach. It gets |
// one instruction at a time. Future versions should pipeline |
// fetches and perhaps even cache results--this doesn't do that. It |
// should, however, be simple enough to get things running. |
// fetches and perhaps even cache results--this doesn't do that. |
// It should, however, be simple enough to get things running. |
// |
// The interface is fascinating. The 'i_pc' input wire is just a |
// suggestion of what to load. Other wires may be loaded instead. i_pc |
// is what must be output, not necessarily input. |
// The interface is fascinating. The 'i_pc' input wire is just |
// a suggestion of what to load. Other wires may be loaded |
// instead. i_pc is what must be output, not necessarily input. |
// |
// 20150919 -- Added support for the WB error signal. When reading an |
// instruction results in this signal being raised, the pipefetch module |
// will set an illegal instruction flag to be returned to the CPU together |
// with the instruction. Hence, the ZipCPU can trap on it if necessary. |
// 20150919 -- Added support for the WB error signal. When reading an |
// instruction results in this signal being raised, the pipefetch |
// module will set an illegal instruction flag to be returned to |
// the CPU together with the instruction. Hence, the ZipCPU |
// can trap on it if necessary. |
// |
// 20171020 -- Added a formal proof to prove that the module works. This |
// also involved adding a req_addr register, and the logic associated |
// with it. |
// |
// 20171113 -- Removed the req_addr register, replacing it with a bus abort |
// capability. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015,2017-2019, Gisselquist Technology, LLC |
// Copyright (C) 2015,2017, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
54,40 → 48,33
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
// |
// |
module prefetch(i_clk, i_reset, i_new_pc, i_clear_cache, i_stalled_n, i_pc, |
o_insn, o_pc, o_valid, o_illegal, |
// Flash requires a minimum of 4 clocks per byte to read, so that would be |
// 4*(4bytes/32bit word) = 16 clocks per word read---and that's in pipeline |
// mode which this prefetch does not support. In non--pipelined mode, the |
// flash will require (16+6+6)*2 = 56 clocks plus 16 clocks per word read, |
// or 72 clocks to fetch one instruction. |
module prefetch(i_clk, i_rst, i_new_pc, i_clear_cache, i_stalled_n, i_pc, |
o_i, o_pc, o_valid, o_illegal, |
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, |
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data); |
parameter ADDRESS_WIDTH=30, DATA_WIDTH=32; |
localparam AW=ADDRESS_WIDTH, |
DW=DATA_WIDTH; |
input wire i_clk, i_reset; |
// CPU interaction wires |
input wire i_new_pc, i_clear_cache, i_stalled_n; |
// We ignore i_pc unless i_new_pc is true as well |
input wire [(AW+1):0] i_pc; |
output reg [(DW-1):0] o_insn; // Instruction read from WB |
output wire [(AW+1):0] o_pc; // Address of that instruction |
output reg o_valid; // If the output is valid |
output reg o_illegal; // Result is from a bus err |
parameter ADDRESS_WIDTH=32; |
localparam AW=ADDRESS_WIDTH; |
input i_clk, i_rst, i_new_pc, i_clear_cache, |
i_stalled_n; |
input [(AW-1):0] i_pc; |
output reg [31:0] o_i; |
output wire [(AW-1):0] o_pc; |
output reg o_valid; |
// Wishbone outputs |
output reg o_wb_cyc, o_wb_stb; |
output wire o_wb_we; |
output reg [(AW-1):0] o_wb_addr; |
output wire [(DW-1):0] o_wb_data; |
output wire [31:0] o_wb_data; |
// And return inputs |
input wire i_wb_ack, i_wb_stall, i_wb_err; |
input wire [(DW-1):0] i_wb_data; |
input i_wb_ack, i_wb_stall, i_wb_err; |
input [31:0] i_wb_data; |
output reg o_illegal; |
|
// Declare local variables |
reg invalid; |
|
// These are kind of obligatory outputs when dealing with a bus, that |
// we'll set them here. Nothing's going to pay attention to these, |
// though, this is primarily for form. |
assign o_wb_we = 1'b0; |
assign o_wb_data = 32'h0000; |
|
96,497 → 83,56
// pipeline this, but for now let's just do one at a time. |
initial o_wb_cyc = 1'b0; |
initial o_wb_stb = 1'b0; |
initial o_wb_addr= 0; |
always @(posedge i_clk) |
if ((i_reset)||((o_wb_cyc)&&((i_wb_ack)||(i_wb_err)))) |
begin |
// End any bus cycle on a reset, or a return ACK |
// or error. |
o_wb_cyc <= 1'b0; |
o_wb_stb <= 1'b0; |
end else if ((!o_wb_cyc)&&( |
// Start if the last instruction output was |
// accepted, *and* it wasn't a bus error |
// response |
((i_stalled_n)&&(!o_illegal)) |
// Start if the last bus result ended up |
// invalid |
||(invalid) |
// Start on any request for a new address |
||(i_new_pc))) |
begin |
// Initiate a bus transaction |
o_wb_cyc <= 1'b1; |
o_wb_stb <= 1'b1; |
end else if (o_wb_cyc) |
begin |
// If our request has been accepted, then drop the |
// strobe line |
if (!i_wb_stall) |
o_wb_stb <= 1'b0; |
|
// Abort on new-pc |
// ... clear_cache is identical, save that it will |
// immediately be followed by a new PC, so we don't |
// need to worry about that other than to drop |
// CYC and STB here. |
if (i_new_pc) |
if ((i_rst)||(i_wb_ack)||(i_wb_err)) |
begin |
o_wb_cyc <= 1'b0; |
o_wb_stb <= 1'b0; |
end else if ((!o_wb_cyc)&&((i_stalled_n)||(!o_valid))) |
begin // Initiate a bus cycle |
o_wb_cyc <= 1'b1; |
o_wb_stb <= 1'b1; |
end else if (o_wb_cyc) // Independent of ce |
begin |
if (~i_wb_stall) |
o_wb_stb <= 1'b0; |
end |
end |
|
// |
// If during the current bus request, a command came in from the CPU |
// that will invalidate the results of that request, then we need to |
// keep track of an "invalid" flag to remember that and so squash |
// the result. |
// |
reg invalid; |
initial invalid = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(!o_wb_cyc)) |
invalid <= 1'b0; |
else if (i_new_pc) |
invalid <= 1'b1; |
if (!o_wb_cyc) |
invalid <= 1'b0; |
else if ((i_new_pc)||(i_clear_cache)) |
invalid <= (!o_wb_stb); |
|
// The wishbone request address, o_wb_addr |
// |
// The rule regarding this address is that it can *only* be changed |
// when no bus request is active. Further, since the CPU is depending |
// upon this value to know what "PC" is associated with the instruction |
// it is processing, we can't change until either the CPU has accepted |
// our result, or it is requesting a new PC (and hence not using the |
// output). |
// |
initial o_wb_addr= 0; |
always @(posedge i_clk) |
if (i_new_pc) |
o_wb_addr <= i_pc[AW+1:2]; |
else if ((o_valid)&&(i_stalled_n)&&(!o_illegal)) |
o_wb_addr <= o_wb_addr + 1'b1; |
if (i_new_pc) |
o_wb_addr <= i_pc; |
else if ((!o_wb_cyc)&&(i_stalled_n)&&(!invalid)) |
o_wb_addr <= o_wb_addr + 1'b1; |
|
// The instruction returned is given by the data returned from the bus. |
always @(posedge i_clk) |
if ((o_wb_cyc)&&(i_wb_ack)) |
o_insn <= i_wb_data; |
if ((o_wb_cyc)&&(i_wb_ack)) |
o_i <= i_wb_data; |
|
// |
// Finally, the flags associated with the prefetch. The rule is that |
// if the output represents a return from the bus, then o_valid needs |
// to be true. o_illegal will be true any time the last bus request |
// resulted in an error. o_illegal is only relevant to the CPU when |
// o_valid is also true, hence o_valid will be true even in the case |
// of a bus error in our request. |
// |
initial o_valid = 1'b0; |
initial o_illegal = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(i_new_pc)||(i_clear_cache)) |
begin |
// On any reset, request for a new PC (i.e. a branch), |
// or a request to clear our cache (i.e. the data |
// in memory may have changed), we invalidate any |
// output. |
o_valid <= 1'b0; |
o_illegal <= 1'b0; |
end else if ((o_wb_cyc)&&((i_wb_ack)||(i_wb_err))) |
begin |
// Otherwise, at the end of our bus cycle, the |
// answer will be valid. Well, not quite. If the |
// user requested something mid-cycle (i_new_pc) |
// or (i_clear_cache), then we'll have to redo the |
// bus request, so we aren't valid. |
// |
o_valid <= 1'b1; |
o_illegal <= ( i_wb_err); |
end else if (i_stalled_n) |
begin |
// Once the CPU accepts any result we produce, clear |
// the valid flag, lest we send two identical |
// instructions to the CPU. |
// |
o_valid <= 1'b0; |
// |
// o_illegal doesn't change ... that way we don't |
// access the bus again until a new address request |
// is given to us, via i_new_pc, or we are asked |
// to check again via i_clear_cache |
// |
// o_illegal <= (!i_stalled_n); |
end |
if (i_rst) |
begin |
o_valid <= 1'b0; |
o_illegal <= 1'b0; |
end else if ((o_wb_cyc)&&(i_wb_ack)) |
begin |
o_valid <= (!i_wb_err)&&(!invalid); |
o_illegal <= ( i_wb_err)&&(!invalid); |
end else if ((i_stalled_n)||(i_clear_cache)) |
begin |
o_valid <= 1'b0; |
o_illegal <= 1'b0; |
end |
|
// The o_pc output shares its value with the (last) wishbone address |
assign o_pc = { o_wb_addr, 2'b00 }; |
|
// Make verilator happy |
// verilator lint_off UNUSED |
wire [1:0] unused; |
assign unused = i_pc[1:0]; |
// verilator lint_on UNUSED |
`ifdef FORMAL |
localparam F_LGDEPTH=2; |
reg f_past_valid; |
wire [(F_LGDEPTH-1):0] f_nreqs, f_nacks, |
f_outstanding; |
reg [(AW-1):0] f_last_pc; |
reg f_last_pc_valid; |
reg [(AW-1):0] f_req_addr; |
|
// |
// |
// Generic setup |
// |
// |
`ifdef PREFETCH |
`define ASSUME assume |
`else |
`define ASSUME assert |
`endif |
|
// Assume a clock |
|
// Keep track of a flag telling us whether or not $past() |
// will return valid results |
initial f_past_valid = 1'b0; |
always @(posedge i_clk) |
f_past_valid = 1'b1; |
|
///////////////////////////////////////////////// |
// |
// |
// Assumptions about our inputs |
// |
// |
///////////////////////////////////////////////// |
|
// Assume we start from a reset condition |
initial `ASSUME(i_reset); |
always @(*) |
if (!f_past_valid) |
`ASSUME(i_reset); |
// Some things to know from the CPU ... there will always be a |
// i_new_pc request following any reset |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_reset))) |
`ASSUME(i_new_pc); |
|
// There will also be a i_new_pc request following any request to clear |
// the cache. |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_clear_cache))) |
`ASSUME(i_new_pc); |
|
// |
// |
// Let's make some assumptions about how long it takes our |
// phantom bus and phantom CPU to respond. |
// |
// These delays need to be long enough to flush out any potential |
// errors, yet still short enough that the formal method doesn't |
// take forever to solve. |
// |
localparam F_CPU_DELAY = 4; |
reg [4:0] f_cpu_delay; |
// First, let's assume that any response from the bus comes back |
// within F_WB_DELAY clocks |
|
// Here's our delay assumption: We'll assume that the |
// wishbone will always respond within F_WB_DELAY clock ticks |
// of the beginning of any cycle. |
// |
// This includes both dropping the stall line, as well as |
// acknowledging any request. While this may not be |
// a reasonable assumption for a piped master, it should |
// work here for us. |
|
// Count the number of clocks it takes the CPU to respond to our |
// instruction. |
always @(posedge i_clk) |
// If no instruction is ready, then keep our counter at zero |
if ((i_reset)||(!o_valid)||(i_stalled_n)) |
f_cpu_delay <= 0; |
else |
// Otherwise, count the clocks the CPU takes to respond |
f_cpu_delay <= f_cpu_delay + 1'b1; |
|
`ifdef PREFETCH |
// Only *assume* that we are less than F_CPU_DELAY if we are not |
// integrated into the CPU |
always @(posedge i_clk) |
assume(f_cpu_delay < F_CPU_DELAY); |
`endif |
|
fwb_master #(.AW(AW), .DW(DW),.F_LGDEPTH(F_LGDEPTH), |
.F_MAX_REQUESTS(1), .F_OPT_SOURCE(1), |
.F_OPT_RMW_BUS_OPTION(0), |
.F_OPT_DISCONTINUOUS(0)) |
f_wbm(i_clk, i_reset, |
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, 4'h0, |
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err, |
f_nreqs, f_nacks, f_outstanding); |
|
///////////////////////////////////////////////// |
// |
// |
// Assertions about our outputs |
// |
// |
///////////////////////////////////////////////// |
// |
// Assertions about our wishbone control outputs first |
// Prefetches don't write |
always @(posedge i_clk) |
if (o_wb_stb) |
assert(!o_wb_we); |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(f_past_valid)) |
&&($past(i_clear_cache,2)) |
&&($past(o_wb_cyc,2))) |
// Make sure any clear-cache transaction is aborted, |
// *and* that no valid instructions get sent to the |
// CPU |
assert((!$past(o_wb_cyc))||(!o_wb_cyc)); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_valid))&&(o_valid)) |
assert(o_wb_addr == $past(o_wb_addr)); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(!i_reset))&&($past(invalid))) |
assert(o_wb_cyc); |
|
// Any time the CPU accepts an instruction, assert that on the |
// valid line will be low on the next clock |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_valid))&&($past(i_stalled_n))) |
assert(!o_valid); |
|
// Since we only change our output on a response from the bus, we |
// need to insist that the item has been read by the CPU before |
// we go looking/asking for a next value. |
// |
// This routine should never be requesting a new instruction when |
// one is valid--lest the CPU never accept the old instruction and we |
// have nothing to do with the data when the bus request returns. |
always @(*) |
if (o_wb_cyc) |
assert(!o_valid); |
|
// If we just got a valid instruction from the wishbone, assert that |
// the instruction is listed as valid on the next instruction cycle |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset)) |
&&($past(o_wb_cyc)) |
&&($past(!i_clear_cache)) |
&&($past(i_wb_ack))&&(!$past(i_wb_err))) |
begin |
if (!invalid) |
assert(o_valid); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_clear_cache))) |
assert(!o_valid); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(f_past_valid)) |
&&($past(i_clear_cache,2)) |
&&($past(o_wb_cyc,2))) |
// Make sure any clear-cache transaction is aborted, |
// *and* that no valid instructions get sent to the |
// CPU |
assert(!o_valid); |
|
// |
// Assertions about our return responses |
// |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset)) |
&&(!$past(i_new_pc))&&(!$past(i_clear_cache)) |
&&($past(o_valid))&&(!$past(i_stalled_n))) |
assert(o_valid == $past(o_valid)); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_valid))&&(o_valid)) |
begin |
assert($stable(o_pc)); |
assert($stable(o_insn)); |
assert($stable(o_illegal)); |
end |
|
// |
// The o_illegal line is the one we use to remind us not to go |
// back and retry the last value if it returned a bus error. Hence, |
// let's assert that this line stays constant any time o_wb_cyc |
// is low, and we haven't received any new requests. |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset)) |
&&(!$past(i_new_pc))&&(!$past(i_clear_cache)) |
&&($past(!o_wb_cyc))) |
assert(o_illegal == $past(o_illegal)); |
|
|
// |
// |
// Let's examine whether or not we "walk" though PC addresses one |
// at a time like we expect. |
// |
initial f_last_pc_valid = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(i_clear_cache)||(i_new_pc)||(invalid)) |
f_last_pc_valid <= 1'b0; |
else if (o_valid) |
f_last_pc_valid <= (!o_illegal); |
|
// initial f_last_pc = 0; |
always @(posedge i_clk) |
if (o_valid) |
f_last_pc <= o_pc[AW+1:2]; |
else if (f_last_pc_valid) |
assert(o_pc[AW+1:2] == f_last_pc + 1'b1); |
|
always @(*) |
assert(o_pc[1:0] == 2'b00); |
|
// If we are producing a new result, and no new-pc or clear cache |
// has come through (i.e. f_last_pc_valid is true), then the resulting |
// PC should be one more than the last time. |
// |
// The o_valid && !$past(o_valid) is necessary because f_last_pc_valid |
// will be different by the second o_valid. |
always @(posedge i_clk) |
if ((f_past_valid)&&(o_valid) |
&&(!$past(o_valid))&&(f_last_pc_valid)) |
assert(o_pc[AW+1:2] == (f_last_pc + 1'b1)); |
|
// Let's also keep track of the address the CPU wants us to return. |
// Any time the CPU branches to a new_pc, we'll record that request. |
// Likewise, any time an instruction is returned from the bus, |
// we'll increment this address so as to automatically walk through |
// memory. |
// |
always @(*) |
assume(i_pc[1:0] == 2'b00); |
initial f_req_addr = 0; |
always @(posedge i_clk) |
if (i_new_pc) |
f_req_addr <= i_pc[AW+1:2]; |
else if ((!invalid)&&(o_wb_cyc)&&(i_wb_ack)&&(!i_wb_err)) |
f_req_addr <= f_req_addr + 1'b1; |
|
// Let's also keep the formal methods on track. Any time we are |
// requesting a value, it should either be from the req_addr, or if |
// not a new value should've come in rendering this one invalid. |
always @(posedge i_clk) |
if (o_wb_cyc) |
assert((invalid)||(f_req_addr == o_wb_addr)); |
// This isn't good enough for induction, so we'll need to |
// constrain this further |
else if ((!o_valid)&&(!i_new_pc)&&(!i_reset)) |
assert(f_req_addr == o_wb_addr); |
|
// In this version, invalid should only ever be high for one cycle. |
// CYC should be high on the cycle following--if ever. |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(invalid))) |
assert(!invalid); |
|
(* anyconst *) reg [AW:0] const_addr; |
(* anyconst *) reg [DW-1:0] const_insn; |
|
wire f_this_addr, f_this_pc, f_this_req, f_this_data; |
assign f_this_addr = (o_wb_addr == const_addr[AW-1:0]); |
assign f_this_pc = (o_pc == { const_addr[AW-1:0], 2'b00 }); |
assign f_this_req = (i_pc == { const_addr[AW-1:0], 2'b00 }); |
assign f_this_data = (i_wb_data == const_insn); |
|
reg f_addr_pending; |
initial f_addr_pending = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
f_addr_pending <= 1'b0; |
else if (!o_wb_cyc) |
f_addr_pending <= 1'b0; |
else if ((o_wb_stb)&&(f_this_addr)) |
begin |
if ((!i_wb_ack)&&(!i_wb_err)) |
f_addr_pending <= 1'b1; |
end |
|
always @(*) |
if ((o_wb_stb)&&(f_this_addr)&&(!i_wb_stall)) |
begin |
if (!const_addr[AW]) |
assume(!i_wb_err); |
else |
assume(!i_wb_ack); |
if (i_wb_ack) |
assume(f_this_data); |
end else if ((o_wb_cyc)&&(f_addr_pending)) |
begin |
if (!const_addr[AW]) |
assume(!i_wb_err); |
else |
assume(!i_wb_ack); |
if (i_wb_ack) |
assume(f_this_data); |
end |
|
always @(*) |
if ((o_valid)&&(f_this_pc)&&(!o_illegal)) |
assert(o_insn == const_insn); |
always @(*) |
if ((o_valid)&&(f_this_pc)) |
assert(o_illegal == const_addr[AW]); |
|
reg f_insn_pending; |
|
initial f_insn_pending = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
f_insn_pending <= 1'b0; |
else if (i_clear_cache) |
f_insn_pending <= 1'b0; |
else if ((i_new_pc)&&(f_this_req)) |
f_insn_pending <= 1'b1; |
else if ((o_valid)||(i_new_pc)) |
f_insn_pending <= 1'b0; |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_wb_cyc))&&(o_wb_cyc)&&(f_insn_pending)) |
assert(f_this_pc); |
|
always @(posedge i_clk) |
if (((f_past_valid)&&($past(o_wb_cyc))&&($past(f_insn_pending))) |
&&(!$past(i_reset))&&(!$past(i_clear_cache)) |
&&(!$past(i_new_pc))) |
begin |
if(!o_wb_cyc) |
assert((o_valid)&&(f_this_pc)); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(o_wb_cyc))&&(!o_wb_cyc)) |
assert(!f_insn_pending); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_wb_cyc))&&(o_wb_cyc)&&(f_this_addr)) |
assert(f_addr_pending); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_wb_cyc))&&(f_insn_pending)) |
assert(f_this_addr); |
`endif |
assign o_pc = o_wb_addr; |
endmodule |
// |
// Usage: (this) (mid) (past) |
// Cells 167 230 175 |
// FDRE 67 97 69 |
// LUT1 1 1 1 |
// LUT2 1 3 3 |
// LUT3 31 63 33 |
// LUT4 5 3 3 |
// LUT5 1 3 3 |
// LUT6 2 1 3 |
// MUXCY 29 29 31 |
// XORCY 30 30 32 |
/core/zipcpu.v
3,7 → 3,7
// Filename: zipcpu.v |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
//{{{ |
// |
// Purpose: This is the top level module holding the core of the Zip CPU |
// together. The Zip CPU is designed to be as simple as possible. |
// (actual implementation aside ...) The instruction set is about as |
49,7 → 49,7
// |
// |
// always @(posedge i_clk) |
// if ((i_reset)||(clear_pipeline)) |
// if ((i_rst)||(clear_pipeline)) |
// (n)_valid = 0 |
// else if (n)_ce |
// (n)_valid = 1 |
68,14 → 68,14
// Note that a stage can stall even if no instruction is loaded into |
// it. |
// |
//}}} |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2019, Gisselquist Technology, LLC |
//{{{ |
// Copyright (C) 2015-2017, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// by the Free Software Foundation, either version 3 of the License, or (at |
90,7 → 90,7
// with this program. (It's in the $(ROOT)/doc directory. Run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
//}}} |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
98,13 → 98,7
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
// |
`define CPU_SUB_OP 4'h0 // also a compare instruction |
`define CPU_AND_OP 4'h1 // also a test instruction |
`define CPU_BREV_OP 4'h8 |
`define CPU_MOV_OP 4'hd |
// |
`define CPU_CC_REG 4'he |
`define CPU_PC_REG 4'hf |
`define CPU_CLRCACHE_BIT 14 // Set to clear the I-cache, automatically clears |
123,7 → 117,7
`include "cpudefs.v" |
// |
// |
module zipcpu(i_clk, i_reset, i_interrupt, |
module zipcpu(i_clk, i_rst, i_interrupt, |
// Debug interface |
i_halt, i_clear_pf_cache, i_dbg_reg, i_dbg_we, i_dbg_data, |
o_dbg_stall, o_dbg_reg, o_dbg_cc, |
137,14 → 131,12
// Accounting/CPU usage interface |
o_op_stall, o_pf_stall, o_i_count |
`ifdef DEBUG_SCOPE |
, o_debug // , o_dcache_debug |
, o_debug |
`endif |
); |
// Parameters |
//{{{ |
parameter [31:0] RESET_ADDRESS=32'h0100000; |
parameter ADDRESS_WIDTH=30, |
LGICACHE=12; |
LGICACHE=8; |
`ifdef OPT_MULTIPLY |
parameter IMPLEMENT_MPY = `OPT_MULTIPLY; |
`else |
151,64 → 143,30
parameter IMPLEMENT_MPY = 0; |
`endif |
`ifdef OPT_DIVIDE |
parameter [0:0] IMPLEMENT_DIVIDE = 1; |
parameter IMPLEMENT_DIVIDE = 1; |
`else |
parameter [0:0] IMPLEMENT_DIVIDE = 0; |
parameter IMPLEMENT_DIVIDE = 0; |
`endif |
`ifdef OPT_IMPLEMENT_FPU |
parameter [0:0] IMPLEMENT_FPU = 1; |
parameter IMPLEMENT_FPU = 1, |
`else |
parameter [0:0] IMPLEMENT_FPU = 0; |
parameter IMPLEMENT_FPU = 0, |
`endif |
IMPLEMENT_LOCK=1; |
`ifdef OPT_EARLY_BRANCHING |
parameter [0:0] EARLY_BRANCHING = 1; |
parameter EARLY_BRANCHING = 1; |
`else |
parameter [0:0] EARLY_BRANCHING = 0; |
parameter EARLY_BRANCHING = 0; |
`endif |
`ifdef OPT_CIS |
parameter [0:0] OPT_CIS = 1'b1; |
`else |
parameter [0:0] OPT_CIS = 1'b0; |
`endif |
`ifdef OPT_NO_USERMODE |
localparam [0:0] OPT_NO_USERMODE = 1'b1; |
`else |
localparam [0:0] OPT_NO_USERMODE = 1'b0; |
`endif |
`ifdef OPT_PIPELINED |
parameter [0:0] OPT_PIPELINED = 1'b1; |
`else |
parameter [0:0] OPT_PIPELINED = 1'b0; |
`endif |
`ifdef OPT_PIPELINED_BUS_ACCESS |
localparam [0:0] OPT_PIPELINED_BUS_ACCESS = (OPT_PIPELINED); |
`else |
localparam [0:0] OPT_PIPELINED_BUS_ACCESS = 1'b0; |
`endif |
localparam [0:0] OPT_MEMPIPE = OPT_PIPELINED_BUS_ACCESS; |
parameter [0:0] IMPLEMENT_LOCK=1; |
localparam [0:0] OPT_LOCK=(IMPLEMENT_LOCK)&&(OPT_PIPELINED); |
`ifdef OPT_DCACHE |
parameter OPT_LGDCACHE = 10; |
`else |
parameter OPT_LGDCACHE = 0; |
`endif |
localparam [0:0] OPT_DCACHE = (OPT_LGDCACHE > 0); |
|
parameter [0:0] WITH_LOCAL_BUS = 1'b1; |
parameter WITH_LOCAL_BUS = 1; |
localparam AW=ADDRESS_WIDTH; |
localparam [(AW-1):0] RESET_BUS_ADDRESS = RESET_ADDRESS[(AW+1):2]; |
parameter F_LGDEPTH=8; |
|
//}}} |
// I/O declarations |
//{{{ |
input wire i_clk, i_reset, i_interrupt; |
input i_clk, i_rst, i_interrupt; |
// Debug interface -- inputs |
input wire i_halt, i_clear_pf_cache; |
input wire [4:0] i_dbg_reg; |
input wire i_dbg_we; |
input wire [31:0] i_dbg_data; |
input i_halt, i_clear_pf_cache; |
input [4:0] i_dbg_reg; |
input i_dbg_we; |
input [31:0] i_dbg_data; |
// Debug interface -- outputs |
output wire o_dbg_stall; |
output reg [31:0] o_dbg_reg; |
221,9 → 179,9
output wire [31:0] o_wb_data; |
output wire [3:0] o_wb_sel; |
// Wishbone interface -- inputs |
input wire i_wb_ack, i_wb_stall; |
input wire [31:0] i_wb_data; |
input wire i_wb_err; |
input i_wb_ack, i_wb_stall; |
input [31:0] i_wb_data; |
input i_wb_err; |
// Accounting outputs ... to help us count stalls and usage |
output wire o_op_stall; |
output wire o_pf_stall; |
231,9 → 189,7
// |
`ifdef DEBUG_SCOPE |
output reg [31:0] o_debug; |
// output wire [31:0] o_dcache_debug; |
`endif |
//}}} |
|
|
// Registers |
246,7 → 202,11
// that logic. |
// |
(* ram_style = "distributed" *) |
reg [31:0] regset [0:(OPT_NO_USERMODE)? 15:31]; |
`ifdef OPT_NO_USERMODE |
reg [31:0] regset [0:15]; |
`else |
reg [31:0] regset [0:31]; |
`endif |
|
// Condition codes |
// (BUS, TRAP,ILL,BREAKEN,STEP,GIE,SLEEP ), V, N, C, Z |
253,7 → 213,7
reg [3:0] flags, iflags; |
wire [14:0] w_uflags, w_iflags; |
reg break_en, step, sleep, r_halted; |
wire break_pending, trap, gie, ubreak, pending_interrupt; |
wire break_pending, trap, gie, ubreak; |
wire w_clear_icache, ill_err_u; |
reg ill_err_i; |
reg ibus_err_flag; |
263,7 → 223,7
wire ihalt_phase, uhalt_phase; |
|
// The master chip enable |
wire master_ce, master_stall; |
wire master_ce; |
|
// |
// |
270,30 → 230,19
// PIPELINE STAGE #1 :: Prefetch |
// Variable declarations |
// |
//{{{ |
reg [(AW+1):0] pf_pc; |
wire [(AW+1):0] pf_request_address, pf_instruction_pc; |
reg new_pc; |
wire clear_pipeline; |
assign clear_pipeline = new_pc; |
|
reg dcd_stalled; |
wire pf_cyc, pf_stb, pf_we, pf_ack, pf_stall, pf_err; |
wire dcd_stalled; |
wire pf_cyc, pf_stb, pf_we, pf_busy, pf_ack, pf_stall, pf_err; |
wire [(AW-1):0] pf_addr; |
wire [31:0] pf_data; |
wire [31:0] pf_instruction; |
wire pf_valid, pf_gie, pf_illegal; |
wire pf_stalled; |
wire pf_new_pc; |
`ifdef FORMAL |
wire [31:0] f_dcd_insn_word; |
wire f_dcd_insn_gie; |
reg [31:0] f_op_insn_word; |
reg [31:0] f_alu_insn_word; |
`endif |
wire [(AW-1):0] pf_instruction_pc; |
wire pf_valid, pf_gie, pf_illegal; |
|
assign clear_pipeline = new_pc; |
//}}} |
|
// |
// |
// PIPELINE STAGE #2 :: Instruction Decode |
300,13 → 249,12
// Variable declarations |
// |
// |
//{{{ |
reg op_valid /* verilator public_flat */, |
op_valid_mem, op_valid_alu; |
reg op_valid_div, op_valid_fpu; |
wire op_stall, dcd_ce, dcd_phase; |
wire [3:0] dcd_opn; |
wire [4:0] dcd_A, dcd_B, dcd_R, dcd_preA, dcd_preB; |
wire [4:0] dcd_A, dcd_B, dcd_R; |
wire dcd_Acc, dcd_Bcc, dcd_Apc, dcd_Bpc, dcd_Rcc, dcd_Rpc; |
wire [3:0] dcd_F; |
wire dcd_wR, dcd_rA, dcd_rB, |
314,21 → 262,17
dcd_wF, dcd_gie, dcd_break, dcd_lock, |
dcd_pipe, dcd_ljmp; |
wire dcd_valid; |
wire [AW+1:0] dcd_pc /* verilator public_flat */; |
wire [AW:0] dcd_pc /* verilator public_flat */; |
wire [31:0] dcd_I; |
wire dcd_zI; // true if dcd_I == 0 |
wire dcd_A_stall, dcd_B_stall, dcd_F_stall; |
|
wire dcd_illegal; |
wire dcd_early_branch, dcd_early_branch_stb; |
wire [(AW+1):0] dcd_branch_pc; |
wire dcd_early_branch; |
wire [(AW-1):0] dcd_branch_pc; |
|
wire dcd_sim; |
wire [22:0] dcd_sim_immv; |
wire prelock_stall; |
wire cc_invalid_for_dcd; |
wire pending_sreg_write; |
//}}} |
|
|
// |
338,29 → 282,20
// |
// |
// |
//{{{ |
// Now, let's read our operands |
reg [4:0] alu_reg; |
wire [3:0] op_opn; |
reg [4:0] op_R; |
reg op_Rcc; |
reg [4:0] op_Aid, op_Bid; |
reg op_rA, op_rB; |
wire [4:0] op_R; |
reg [31:0] r_op_Av, r_op_Bv; |
reg [(AW+1):0] op_pc; |
wire [31:0] w_op_Av, w_op_Bv, op_Av, op_Bv; |
reg [31:0] w_pcB_v, w_pcA_v; |
reg [31:0] w_op_BnI; |
reg [(AW-1):0] op_pc; |
wire [31:0] w_op_Av, w_op_Bv; |
wire [31:0] op_A_nowait, op_B_nowait, op_Av, op_Bv; |
reg op_wR, op_wF; |
wire op_gie; |
wire [3:0] op_Fl; |
wire op_gie, op_Rcc; |
wire [14:0] op_Fl; |
reg [6:0] r_op_F; |
wire [7:0] op_F; |
wire op_ce, op_phase, op_pipe; |
reg r_op_break; |
reg [3:0] r_op_opn; |
wire w_op_valid; |
wire [8:0] w_cpu_info; |
wire op_ce, op_phase, op_pipe, op_change_data_ce; |
// Some pipeline control wires |
reg op_illegal; |
wire op_break; |
369,10 → 304,7
`ifdef VERILATOR |
reg op_sim /* verilator public_flat */; |
reg [22:0] op_sim_immv /* verilator public_flat */; |
`else |
wire op_sim = 1'b0; |
`endif |
//}}} |
|
|
// |
381,8 → 313,7
// Variable declarations |
// |
// |
//{{{ |
wire [(AW+1):0] alu_pc; |
wire [(AW-1):0] alu_pc; |
reg r_alu_pc_valid, mem_pc_valid; |
wire alu_pc_valid; |
wire alu_phase; |
395,10 → 326,11
wire alu_gie, alu_illegal; |
|
|
wire mem_ce, mem_stalled; |
wire mem_pipe_stalled; |
wire mem_valid, mem_ack, mem_stall, mem_err, bus_err, |
mem_cyc_gbl, mem_cyc_lcl, mem_stb_gbl, mem_stb_lcl, mem_we; |
|
wire mem_ce, mem_stalled; |
wire mem_pipe_stalled; |
wire mem_valid, mem_ack, mem_stall, mem_err, bus_err, |
mem_cyc_gbl, mem_cyc_lcl, mem_stb_gbl, mem_stb_lcl, mem_we; |
wire [4:0] mem_wreg; |
|
wire mem_busy, mem_rdbusy; |
406,62 → 338,43
wire [31:0] mem_data, mem_result; |
wire [3:0] mem_sel; |
|
wire div_ce, div_error, div_busy, div_valid; |
wire div_ce, div_error, div_busy, div_valid; |
wire [31:0] div_result; |
wire [3:0] div_flags; |
|
wire fpu_ce, fpu_error, fpu_busy, fpu_valid; |
assign div_ce = (master_ce)&&(!clear_pipeline)&&(op_valid_div) |
&&(!mem_rdbusy)&&(!div_busy)&&(!fpu_busy) |
&&(set_cond); |
|
wire fpu_ce, fpu_error, fpu_busy, fpu_valid; |
wire [31:0] fpu_result; |
wire [3:0] fpu_flags; |
reg adf_ce_unconditional; |
|
wire bus_lock; |
assign fpu_ce = (master_ce)&&(!clear_pipeline)&&(op_valid_fpu) |
&&(!mem_rdbusy)&&(!div_busy)&&(!fpu_busy) |
&&(set_cond); |
|
reg dbgv, dbg_clear_pipe; |
reg [31:0] dbg_val; |
wire adf_ce_unconditional; |
|
assign div_ce = (op_valid_div)&&(adf_ce_unconditional)&&(set_cond); |
assign fpu_ce = (IMPLEMENT_FPU)&&(op_valid_fpu)&&(adf_ce_unconditional)&&(set_cond); |
|
//}}} |
|
// |
// |
// PIPELINE STAGE #5 :: Write-back |
// Variable declarations |
// |
//{{{ |
wire wr_reg_ce, wr_flags_ce, wr_write_pc, wr_write_cc, |
wr_write_scc, wr_write_ucc; |
wire [4:0] wr_reg_id; |
wire [31:0] wr_gpreg_vl, wr_spreg_vl; |
wire w_switch_to_interrupt, w_release_from_interrupt; |
wire w_switch_to_interrupt, w_release_from_interrupt; |
reg [(AW+1):0] ipc; |
wire [(AW+1):0] upc; |
reg last_write_to_cc; |
wire cc_write_hold; |
reg r_clear_icache; |
//}}} |
|
`ifdef FORMAL |
wire [F_LGDEPTH-1:0] |
f_gbl_arb_nreqs, f_gbl_arb_nacks, f_gbl_arb_outstanding, |
f_lcl_arb_nreqs, f_lcl_arb_nacks, f_lcl_arb_outstanding, |
f_gbl_mem_nreqs, f_gbl_mem_nacks, f_gbl_mem_outstanding, |
f_lcl_mem_nreqs, f_lcl_mem_nacks, f_lcl_mem_outstanding, |
f_gbl_pf_nreqs, f_gbl_pf_nacks, f_gbl_pf_outstanding, |
f_lcl_pf_nreqs, f_lcl_pf_nacks, f_lcl_pf_outstanding, |
f_mem_nreqs, f_mem_nacks, f_mem_outstanding; |
reg f_pf_nreqs, f_pf_nacks, f_pf_outstanding; |
wire f_mem_pc; |
`endif |
|
|
// |
// MASTER: clock enable. |
// |
assign master_ce = ((!i_halt)||(alu_phase)) |
&&(!cc_write_hold)&&(!o_break)&&(!sleep); |
assign master_ce = ((!i_halt)||(alu_phase))&&(!o_break)&&(!sleep); |
|
|
// |
475,50 → 388,45
// PIPELINE STAGE #2 :: Instruction Decode |
// Calculate stall conditions |
|
always @(*) |
if (OPT_PIPELINED) |
dcd_stalled = (dcd_valid)&&(op_stall); |
else |
dcd_stalled = (!master_ce)||(ill_err_i)||(dcd_valid)||(op_valid) |
||(ibus_err_flag)||(idiv_err_flag) |
||(alu_busy)||(div_busy)||(fpu_busy)||(mem_busy); |
`ifdef OPT_PIPELINED |
assign dcd_stalled = (dcd_valid)&&(op_stall); |
`else // Not pipelined -- either double or single fetch |
assign dcd_stalled = (dcd_valid)&&(op_stall); |
`endif |
// |
// PIPELINE STAGE #3 :: Read Operands |
// Calculate stall conditions |
//{{{ |
generate if (OPT_PIPELINED) |
begin : GEN_OP_STALL |
reg r_cc_invalid_for_dcd; |
always @(posedge i_clk) |
r_cc_invalid_for_dcd <= |
(set_cond)&&(op_valid) |
&&((op_wF)||((op_wR)&&(op_R[4:0] == { op_gie, `CPU_CC_REG }))) |
||((r_cc_invalid_for_dcd) |
&&((alu_busy)||(mem_rdbusy)||(div_busy)||(fpu_busy))); |
wire prelock_stall; |
`ifdef OPT_PIPELINED |
reg cc_invalid_for_dcd; |
always @(posedge i_clk) |
cc_invalid_for_dcd <= (wr_flags_ce) |
||(wr_reg_ce)&&(wr_reg_id[3:0] == `CPU_CC_REG) |
||(op_valid)&&((op_wF)||((op_wR)&&(op_R[3:0] == `CPU_CC_REG))) |
||((alu_wF)||((alu_wR)&&(alu_reg[3:0] == `CPU_CC_REG))) |
||(mem_busy)||(div_busy)||(fpu_busy); |
|
assign cc_invalid_for_dcd = r_cc_invalid_for_dcd; |
|
reg r_pending_sreg_write; |
initial r_pending_sreg_write = 1'b0; |
always @(posedge i_clk) |
if (clear_pipeline) |
r_pending_sreg_write <= 1'b0; |
else if (((adf_ce_unconditional)||(mem_ce)) |
&&(set_cond)&&(!op_illegal) |
&&(op_wR) |
&&(op_R[3:1] == 3'h7) |
&&(op_R[4:0] != { gie, 4'hf })) |
r_pending_sreg_write <= 1'b1; |
else if ((!mem_rdbusy)&&(!alu_busy)) |
r_pending_sreg_write <= 1'b0; |
|
assign pending_sreg_write = r_pending_sreg_write; |
|
assign op_stall = (op_valid)&&( |
//{{{ |
// Only stall if we're loaded w/validins and the |
// next stage is accepting our instruction |
(!adf_ce_unconditional)&&(!mem_ce) |
assign op_stall = (op_valid)&&( // Only stall if we're loaded w/validins |
// Stall if we're stopped, and not allowed to execute |
// an instruction |
// (!master_ce) // Already captured in alu_stall |
// |
// Stall if going into the ALU and the ALU is stalled |
// i.e. if the memory is busy, or we are single |
// stepping. This also includes our stalls for |
// op_break and op_lock, so we don't need to |
// include those as well here. |
// This also includes whether or not the divide or |
// floating point units are busy. |
(alu_stall) |
||(((op_valid_div)||(op_valid_fpu)) |
&&(!adf_ce_unconditional)) |
// |
// Stall if we are going into memory with an operation |
// that cannot be pipelined, and the memory is |
// already busy |
||(mem_stalled) // &&(op_valid_mem) part of mem_stalled |
||(op_Rcc) |
) |
||(dcd_valid)&&( |
// Stall if we need to wait for an operand A |
533,36 → 441,25
// CC register |
||(dcd_F_stall) |
); |
//}}} |
assign op_ce = ((dcd_valid)||(dcd_illegal)||(dcd_early_branch))&&(!op_stall); |
assign op_ce = ((dcd_valid)||(dcd_illegal)||(dcd_early_branch))&&(!op_stall); |
|
end else begin // !OPT_PIPELINED |
`else |
assign op_stall = (alu_busy)||(div_busy)||(fpu_busy)||(wr_reg_ce) |
||(mem_busy)||(op_valid)||(!master_ce)||(wr_flags_ce); |
assign op_ce = ((dcd_valid)||(dcd_illegal)||(dcd_early_branch))&&(!op_stall); |
`endif |
|
assign op_stall = 1'b0; // (o_break)||(pending_interrupt); |
assign op_ce = ((dcd_valid)||(dcd_early_branch))&&(!op_stall); |
assign pending_sreg_write = 1'b0; |
assign cc_invalid_for_dcd = 1'b0; |
|
// Verilator lint_off UNUSED |
wire [1:0] pipe_unused; |
assign pipe_unused = { cc_invalid_for_dcd, |
pending_sreg_write }; |
// Verilator lint_on UNUSED |
end endgenerate |
|
// BUT ... op_ce is too complex for many of the data operations. So |
// let's make their circuit enable code simpler. In particular, if |
// op_ doesn't need to be preserved, we can change it all we want |
// ... right? The clear_pipeline code, for example, really only needs |
// to determine whether op_valid is true. |
// assign op_change_data_ce = (!op_stall); |
//}}} |
assign op_change_data_ce = (!op_stall); |
|
// |
// PIPELINE STAGE #4 :: ALU / Memory |
// Calculate stall conditions |
// |
//{{{ |
// 1. Basic stall is if the previous stage is valid and the next is |
// busy. |
// 2. Also stall if the prior stage is valid and the master clock enable |
572,31 → 469,18
// 4. Last case: Stall if we would otherwise move a break instruction |
// through the ALU. Break instructions are not allowed through |
// the ALU. |
generate if (OPT_PIPELINED) |
begin : GEN_ALU_STALL |
assign alu_stall = (((master_stall)||(mem_rdbusy))&&(op_valid_alu)) //Case 1&2 |
||(wr_reg_ce)&&(wr_write_cc); |
// assign // alu_ce = (master_ce)&&(op_valid_alu)&&(!alu_stall) |
// &&(!clear_pipeline)&&(!op_illegal) |
// &&(!pending_sreg_write) |
// &&(!alu_sreg_stall); |
assign alu_ce = (adf_ce_unconditional)&&(op_valid_alu); |
|
// Verilator lint_off unused |
wire unused_alu_stall = alu_stall; |
// Verilator lint_on unused |
end else begin |
|
assign alu_stall = (master_stall); |
//assign alu_ce = (master_ce)&&(op_valid_alu) |
// &&(!clear_pipeline) |
// &&(!alu_stall); |
assign alu_ce = (adf_ce_unconditional)&&(op_valid_alu); |
|
// Verilator lint_off unused |
wire unused_alu_stall = alu_stall; |
// Verilator lint_on unused |
end endgenerate |
`ifdef OPT_PIPELINED |
assign alu_stall = (((!master_ce)||(mem_rdbusy)||(alu_busy))&&(op_valid_alu)) //Case 1&2 |
||(prelock_stall) |
||((op_valid)&&(op_break)) |
||(wr_reg_ce)&&(wr_write_cc) |
||(div_busy)||(fpu_busy); |
assign alu_ce = (master_ce)&&(op_valid_alu)&&(!alu_stall) |
&&(!clear_pipeline); |
`else |
assign alu_stall = (op_valid_alu)&&((!master_ce)||(op_break)); |
assign alu_ce = (master_ce)&&(op_valid_alu)&&(!alu_stall)&&(!clear_pipeline); |
`endif |
// |
|
// |
606,55 → 490,38
assign mem_ce = (master_ce)&&(op_valid_mem)&&(!mem_stalled) |
&&(!clear_pipeline); |
|
generate if (OPT_PIPELINED_BUS_ACCESS) |
begin |
|
assign mem_stalled = (master_stall)||((op_valid_mem)&&( |
`ifdef OPT_PIPELINED_BUS_ACCESS |
assign mem_stalled = (!master_ce)||(alu_busy)||((op_valid_mem)&&( |
(mem_pipe_stalled) |
||(bus_err)||(div_error) |
||(prelock_stall) |
||((!op_pipe)&&(mem_busy)) |
||(div_busy) |
||(fpu_busy) |
// Stall waiting for flags to be valid |
// Or waiting for a write to the PC register |
// Or CC register, since that can change the |
// PC as well |
||((wr_reg_ce) |
||((wr_reg_ce)&&(wr_reg_id[4] == op_gie) |
&&((wr_write_pc)||(wr_write_cc))))); |
end else if (OPT_PIPELINED) |
begin |
assign mem_stalled = (master_stall)||((op_valid_mem)&&( |
(bus_err)||(div_error)||(mem_busy) |
`else |
`ifdef OPT_PIPELINED |
assign mem_stalled = (mem_busy)||((op_valid_mem)&&( |
(!master_ce) |
// Stall waiting for flags to be valid |
// Or waiting for a write to the PC register |
// Or CC register, since that can change the |
// PC as well |
||((wr_reg_ce) |
&&((wr_write_pc)||(wr_write_cc))))); |
end else begin |
||((wr_reg_ce)&&(wr_reg_id[4] == op_gie)&&((wr_write_pc)||(wr_write_cc))))); |
`else |
assign mem_stalled = (op_valid_mem)&&(!master_ce); |
`endif |
`endif |
|
assign mem_stalled = (master_stall); |
|
end endgenerate |
//}}} |
|
assign master_stall = (!master_ce)||(!op_valid)||(ill_err_i) |
||(ibus_err_flag)||(idiv_err_flag) |
||(pending_interrupt)&&(!alu_phase) |
||(alu_busy)||(div_busy)||(fpu_busy)||(op_break) |
||((OPT_PIPELINED)&&( |
((OPT_LOCK)&&(prelock_stall)) |
||((mem_busy)&&(op_illegal)) |
||((mem_busy)&&(op_valid_div)) |
||(alu_illegal)||(o_break))); |
|
|
// ALU, DIV, or FPU CE ... equivalent to the OR of all three of these |
always @(*) |
if (OPT_PIPELINED) |
adf_ce_unconditional = |
(!master_stall)&&(!op_valid_mem)&&(!mem_rdbusy) |
&&((!mem_busy)||(!op_wR)||(op_R[4:1] != { gie, 3'h7})); |
else |
adf_ce_unconditional = (!master_stall)&&(op_valid)&&(!op_valid_mem); |
assign adf_ce_unconditional = (master_ce)&&(!clear_pipeline)&&(op_valid) |
&&(!op_valid_mem)&&(!mem_rdbusy) |
&&((!op_valid_alu)||(!alu_stall))&&(!op_break) |
&&(!div_busy)&&(!fpu_busy)&&(!clear_pipeline); |
|
// |
// |
661,37 → 528,19
// PIPELINE STAGE #1 :: Prefetch |
// |
// |
//{{{ |
wire pf_stalled; |
assign pf_stalled = (dcd_stalled)||(dcd_phase); |
|
assign pf_new_pc = (new_pc)||((dcd_early_branch_stb)&&(!clear_pipeline)); |
wire pf_new_pc; |
assign pf_new_pc = (new_pc)||((dcd_early_branch)&&(!clear_pipeline)); |
|
assign pf_request_address = ((dcd_early_branch_stb)&&(!clear_pipeline)) |
? dcd_branch_pc:pf_pc; |
wire [(AW-1):0] pf_request_address; |
assign pf_request_address = ((dcd_early_branch)&&(!clear_pipeline)) |
? dcd_branch_pc:pf_pc[(AW+1):2]; |
assign pf_gie = gie; |
`ifdef FORMAL |
abs_prefetch #(ADDRESS_WIDTH) |
//{{{ |
pf(i_clk, (i_reset), pf_new_pc, w_clear_icache, |
(!pf_stalled), |
pf_request_address, |
pf_instruction, pf_instruction_pc, |
pf_valid, |
pf_cyc, pf_stb, pf_we, pf_addr, pf_data, |
pf_ack, pf_stall, pf_err, i_wb_data, |
pf_illegal); |
always @(*) |
begin |
f_pf_nreqs = 0; |
f_pf_nacks = 0; |
f_pf_outstanding = 0; |
end |
//}}} |
`else |
`ifdef OPT_SINGLE_FETCH |
prefetch #(ADDRESS_WIDTH) |
//{{{ |
pf(i_clk, (i_reset), pf_new_pc, w_clear_icache, |
pf(i_clk, (i_rst), pf_new_pc, w_clear_icache, |
(!pf_stalled), |
pf_request_address, |
pf_instruction, pf_instruction_pc, |
698,13 → 547,14
pf_valid, pf_illegal, |
pf_cyc, pf_stb, pf_we, pf_addr, pf_data, |
pf_ack, pf_stall, pf_err, i_wb_data); |
//}}} |
|
`else |
`ifdef OPT_DOUBLE_FETCH |
|
wire [1:0] pf_dbg; |
dblfetch #(ADDRESS_WIDTH) |
//{{{ |
pf(i_clk, i_reset, pf_new_pc, w_clear_icache, |
pf(i_clk, i_rst, pf_new_pc, |
w_clear_icache, |
(!pf_stalled), |
pf_request_address, |
pf_instruction, pf_instruction_pc, |
712,14 → 562,12
pf_cyc, pf_stb, pf_we, pf_addr, pf_data, |
pf_ack, pf_stall, pf_err, i_wb_data, |
pf_illegal); |
//}}} |
|
`else // Not single fetch and not double fetch |
|
`ifdef OPT_TRADITIONAL_PFCACHE |
pfcache #(LGICACHE, ADDRESS_WIDTH) |
//{{{ |
pf(i_clk, i_reset, pf_new_pc, w_clear_icache, |
pf(i_clk, i_rst, pf_new_pc, w_clear_icache, |
// dcd_pc, |
(!pf_stalled), |
pf_request_address, |
727,71 → 575,65
pf_cyc, pf_stb, pf_we, pf_addr, pf_data, |
pf_ack, pf_stall, pf_err, i_wb_data, |
pf_illegal); |
//}}} |
`else |
pipefetch #({RESET_BUS_ADDRESS, 2'b00}, LGICACHE, ADDRESS_WIDTH) |
//{{{ |
pf(i_clk, i_reset, pf_new_pc, |
pipefetch #(RESET_BUS_ADDRESS, LGICACHE, ADDRESS_WIDTH) |
pf(i_clk, i_rst, pf_new_pc, |
w_clear_icache, (!pf_stalled), |
(new_pc)?pf_pc:dcd_branch_pc, |
(new_pc)?pf_pc[(AW+1):2]:dcd_branch_pc, |
pf_instruction, pf_instruction_pc, pf_valid, |
pf_cyc, pf_stb, pf_we, pf_addr, pf_data, |
pf_ack, pf_stall, pf_err, i_wb_data, |
(mem_cyc_lcl)||(mem_cyc_gbl), |
pf_illegal); |
//}}} |
`endif // OPT_TRADITIONAL_CACHE |
`endif // OPT_DOUBLE_FETCH |
`endif // OPT_SINGLE_FETCH |
`endif // FORMAL |
//}}} |
|
// |
// |
// PIPELINE STAGE #2 :: Instruction Decode |
// |
// |
//{{{ |
assign dcd_ce =((OPT_PIPELINED)&&(!dcd_valid))||(!dcd_stalled); |
idecode #(.ADDRESS_WIDTH(AW), |
.OPT_MPY((IMPLEMENT_MPY!=0)? 1'b1:1'b0), |
.OPT_PIPELINED(OPT_PIPELINED), |
.OPT_EARLY_BRANCHING(EARLY_BRANCHING), |
.OPT_DIVIDE(IMPLEMENT_DIVIDE), |
.OPT_FPU(IMPLEMENT_FPU), |
.OPT_LOCK(OPT_LOCK), |
.OPT_OPIPE(OPT_PIPELINED_BUS_ACCESS), |
.OPT_NO_USERMODE(OPT_NO_USERMODE), |
`ifdef VERILATOR |
.OPT_SIM(1'b1), |
`else |
.OPT_SIM(1'b0), |
`endif |
.OPT_CIS(OPT_CIS)) |
instruction_decoder(i_clk, |
(i_reset)||(clear_pipeline)||(w_clear_icache), |
assign dcd_ce = (!dcd_valid)||(!dcd_stalled); |
idecode #(AW, IMPLEMENT_MPY, EARLY_BRANCHING, IMPLEMENT_DIVIDE, |
IMPLEMENT_FPU) |
instruction_decoder(i_clk, |
(clear_pipeline)||(w_clear_icache), |
dcd_ce, |
dcd_stalled, pf_instruction, pf_gie, |
pf_instruction_pc, pf_valid, pf_illegal, |
pf_instruction_pc, pf_valid, pf_illegal, |
dcd_valid, dcd_phase, |
dcd_illegal, dcd_pc, |
dcd_illegal, dcd_pc, dcd_gie, |
{ dcd_Rcc, dcd_Rpc, dcd_R }, |
{ dcd_Acc, dcd_Apc, dcd_A }, |
{ dcd_Bcc, dcd_Bpc, dcd_B }, |
dcd_preA, dcd_preB, |
dcd_I, dcd_zI, dcd_F, dcd_wF, dcd_opn, |
dcd_ALU, dcd_M, dcd_DIV, dcd_FP, dcd_break, dcd_lock, |
dcd_wR,dcd_rA, dcd_rB, |
dcd_early_branch, dcd_early_branch_stb, |
dcd_early_branch, |
dcd_branch_pc, dcd_ljmp, |
dcd_pipe, |
dcd_sim, dcd_sim_immv |
`ifdef FORMAL |
, f_dcd_insn_word, f_dcd_insn_gie |
dcd_sim, dcd_sim_immv); |
|
`ifdef OPT_PIPELINED_BUS_ACCESS |
reg r_op_pipe; |
|
initial r_op_pipe = 1'b0; |
// To be a pipeable operation, there must be |
// two valid adjacent instructions |
// Both must be memory instructions |
// Both must be writes, or both must be reads |
// Both operations must be to the same identical address, |
// or at least a single (one) increment above that address |
// |
// However ... we need to know this before this clock, hence this is |
// calculated in the instruction decoder. |
always @(posedge i_clk) |
if (clear_pipeline) |
r_op_pipe <= 1'b0; |
else if (op_ce) |
r_op_pipe <= dcd_pipe; |
else if (mem_ce) // Clear us any time an op_ is clocked in |
r_op_pipe <= 1'b0; |
assign op_pipe = r_op_pipe; |
`else |
assign op_pipe = 1'b0; |
`endif |
); |
assign dcd_gie = pf_gie; |
//}}} |
|
// |
// |
798,204 → 640,122
// PIPELINE STAGE #3 :: Read Operands (Registers) |
// |
// |
//{{{ |
generate if (OPT_PIPELINED_BUS_ACCESS) |
begin : GEN_OP_PIPE |
reg r_op_pipe; |
|
initial r_op_pipe = 1'b0; |
// To be a pipeable operation, there must be |
// two valid adjacent instructions |
// Both must be memory instructions |
// Both must be writes, or both must be reads |
// Both operations must be to the same identical address, |
// or at least a single (one) increment above that |
// address |
// |
// However ... we need to know this before this clock, hence |
// this is calculated in the instruction decoder. |
always @(posedge i_clk) |
if ((clear_pipeline)||(i_halt)) |
r_op_pipe <= 1'b0; |
else if (op_ce) |
r_op_pipe <= (dcd_pipe)&&(op_valid_mem); |
else if ((wr_reg_ce)&&(wr_reg_id == op_Bid[4:0])) |
r_op_pipe <= 1'b0; |
else if (mem_ce) // Clear us any time an op_ is clocked in |
r_op_pipe <= 1'b0; |
|
assign op_pipe = r_op_pipe; |
end else begin |
|
assign op_pipe = 1'b0; |
|
end endgenerate |
|
// `define NO_DISTRIBUTED_RAM |
`ifdef NO_DISTRIBUTED_RAM |
reg [31:0] pre_rewrite_value, pre_op_Av, pre_op_Bv; |
reg pre_rewrite_flag_A, pre_rewrite_flag_B; |
|
always @(posedge i_clk) |
if (dcd_ce) |
begin |
pre_rewrite_flag_A <= (wr_reg_ce)&&(dcd_preA == wr_reg_id); |
pre_rewrite_flag_B <= (wr_reg_ce)&&(dcd_preB == wr_reg_id); |
pre_rewrite_value <= wr_gpreg_vl; |
end |
|
generate if (OPT_NO_USERMODE) |
begin |
always @(posedge i_clk) |
if (dcd_ce) |
begin |
pre_op_Av <= regset[dcd_preA[3:0]]; |
pre_op_Bv <= regset[dcd_preB[3:0]]; |
end |
end else begin |
|
always @(posedge i_clk) |
if (dcd_ce) |
begin |
pre_op_Av <= regset[dcd_preA]; |
pre_op_Bv <= regset[dcd_preB]; |
end |
|
end endgenerate |
|
assign w_op_Av = (pre_rewrite_flag_A) ? pre_rewrite_value : pre_op_Av; |
assign w_op_Bv = (pre_rewrite_flag_B) ? pre_rewrite_value : pre_op_Bv; |
`ifdef OPT_NO_USERMODE |
assign w_op_Av = regset[dcd_A[3:0]]; |
assign w_op_Bv = regset[dcd_B[3:0]]; |
`else |
generate if (OPT_NO_USERMODE) |
begin |
assign w_op_Av = regset[dcd_A[3:0]]; |
assign w_op_Bv = regset[dcd_B[3:0]]; |
end else begin |
|
assign w_op_Av = regset[dcd_A]; |
assign w_op_Bv = regset[dcd_B]; |
|
end endgenerate |
|
// verilator lint_off UNUSED |
wire [9:0] unused_prereg_addrs; |
assign unused_prereg_addrs = { dcd_preA, dcd_preB }; |
// verilator lint_on UNUSED |
assign w_op_Av = regset[dcd_A]; |
assign w_op_Bv = regset[dcd_B]; |
`endif |
|
wire [8:0] w_cpu_info; |
assign w_cpu_info = { |
//{{{ |
1'b1, |
(IMPLEMENT_MPY >0)? 1'b1:1'b0, |
(IMPLEMENT_DIVIDE >0)? 1'b1:1'b0, |
(IMPLEMENT_FPU >0)? 1'b1:1'b0, |
OPT_PIPELINED, |
`ifdef OPT_PIPELINED |
1'b1, |
`else |
1'b0, |
`endif |
`ifdef OPT_TRADITIONAL_CACHE |
1'b1, |
`else |
1'b0, |
`endif |
(EARLY_BRANCHING > 0)? 1'b1:1'b0, |
OPT_PIPELINED_BUS_ACCESS, |
OPT_CIS |
`ifdef OPT_EARLY_BRANCHING |
1'b1, |
`else |
1'b0, |
`endif |
`ifdef OPT_PIPELINED_BUS_ACCESS |
1'b1, |
`else |
1'b0, |
`endif |
`ifdef OPT_CIS |
1'b1 |
`else |
1'b0 |
`endif |
}; |
//}}} |
|
always @(*) |
if ((OPT_NO_USERMODE)||(dcd_A[4] == dcd_gie)) |
w_pcA_v[(AW+1):0] = { dcd_pc[AW+1:2], 2'b00 }; |
else |
w_pcA_v[(AW+1):0] = { upc[(AW+1):2], uhalt_phase, 1'b0 }; |
|
wire [31:0] w_pcA_v; |
assign w_pcA_v[(AW+1):0] = { (dcd_A[4] == dcd_gie) |
? { dcd_pc[AW:1], 2'b00 } |
: { upc[(AW+1):2], uhalt_phase, 1'b0 } }; |
generate |
if (AW < 30) |
always @(*) |
w_pcA_v[31:(AW+2)] = 0; |
assign w_pcA_v[31:(AW+2)] = 0; |
endgenerate |
|
generate if (OPT_PIPELINED) |
begin : OPV |
initial op_R = 0; |
initial op_Aid = 0; |
initial op_Bid = 0; |
initial op_rA = 0; |
initial op_rB = 0; |
initial op_Rcc = 0; |
always @(posedge i_clk) |
`ifdef OPT_PIPELINED |
reg [4:0] op_Aid, op_Bid; |
reg op_rA, op_rB; |
always @(posedge i_clk) |
if (op_ce) |
begin |
op_R <= dcd_R; |
op_Aid <= dcd_A; |
op_Bid <= dcd_B; |
op_rA <= (dcd_rA)&&(!dcd_early_branch)&&(!dcd_illegal); |
op_rB <= (dcd_rB)&&(!dcd_early_branch)&&(!dcd_illegal); |
op_Rcc <= (dcd_Rcc)&&(dcd_wR)&&(dcd_R[4]==dcd_gie); |
op_rA <= dcd_rA; |
op_rB <= dcd_rB; |
end |
`endif |
|
end else begin |
|
always @(*) |
always @(posedge i_clk) |
if (op_ce) |
begin |
op_R = dcd_R; |
op_Aid = dcd_A; |
op_Bid = dcd_B; |
op_rA = dcd_rA; |
op_rB = dcd_rB; |
op_Rcc = (dcd_Rcc)&&(dcd_wR)&&(dcd_R[4]==dcd_gie); |
`ifdef OPT_PIPELINED |
if ((wr_reg_ce)&&(wr_reg_id == dcd_A)) |
r_op_Av <= wr_gpreg_vl; |
else |
`endif |
if (dcd_Apc) |
r_op_Av <= w_pcA_v; |
else if (dcd_Acc) |
r_op_Av <= { w_cpu_info, w_op_Av[22:16], 1'b0, (dcd_A[4])?w_uflags:w_iflags }; |
else |
r_op_Av <= w_op_Av; |
`ifdef OPT_PIPELINED |
end else |
begin |
if ((wr_reg_ce)&&(wr_reg_id == op_Aid)&&(op_rA)) |
r_op_Av <= wr_gpreg_vl; |
`endif |
end |
|
end endgenerate |
|
|
always @(posedge i_clk) |
if ((!OPT_PIPELINED)||(op_ce)) |
begin |
if ((OPT_PIPELINED)&&(wr_reg_ce)&&(wr_reg_id == dcd_A)) |
r_op_Av <= wr_gpreg_vl; |
else if (dcd_Apc) |
r_op_Av <= w_pcA_v; |
else if (dcd_Acc) |
r_op_Av <= { w_cpu_info, w_op_Av[22:16], 1'b0, (dcd_A[4])?w_uflags:w_iflags }; |
else |
r_op_Av <= w_op_Av; |
end else if (OPT_PIPELINED) |
begin |
if ((wr_reg_ce)&&(wr_reg_id == op_Aid)&&(op_rA)) |
r_op_Av <= wr_gpreg_vl; |
end |
|
always @(*) |
if ((OPT_NO_USERMODE)||(dcd_B[4] == dcd_gie)) |
w_pcB_v[(AW+1):0] = { dcd_pc[AW+1:2], 2'b00 }; |
else |
w_pcB_v[(AW+1):0] = { upc[(AW+1):2], uhalt_phase, 1'b0 }; |
wire [31:0] w_op_BnI, w_pcB_v; |
assign w_pcB_v[(AW+1):0] = { (dcd_B[4] == dcd_gie) |
? { dcd_pc[AW:1], 2'b00 } |
: { upc[(AW+1):2], uhalt_phase, 1'b0 } }; |
generate |
if (AW < 30) |
always @(*) |
w_pcB_v[31:(AW+2)] = 0; |
assign w_pcB_v[31:(AW+2)] = 0; |
endgenerate |
|
always @(*) |
if (!dcd_rB) |
w_op_BnI = 0; |
else if ((OPT_PIPELINED)&&(wr_reg_ce)&&(wr_reg_id == dcd_B)) |
w_op_BnI = wr_gpreg_vl; |
else if (dcd_Bcc) |
w_op_BnI = { w_cpu_info, w_op_Bv[22:16], 1'b0, |
(dcd_B[4]) ? w_uflags : w_iflags }; |
else |
w_op_BnI = w_op_Bv; |
assign w_op_BnI = (!dcd_rB) ? 32'h00 |
`ifdef OPT_PIPELINED |
: ((wr_reg_ce)&&(wr_reg_id == dcd_B)) ? wr_gpreg_vl |
`endif |
: ((dcd_Bcc) ? { w_cpu_info, w_op_Bv[22:16], // w_op_B[31:14], |
1'b0, (dcd_B[4])?w_uflags:w_iflags} |
: w_op_Bv); |
|
always @(posedge i_clk) |
if ((!OPT_PIPELINED)||(op_ce)) |
begin |
`ifdef OPT_PIPELINED |
if ((op_ce)&&(dcd_Bpc)&&(dcd_rB)) |
r_op_Bv <= w_pcB_v + { dcd_I[29:0], 2'b00 }; |
else if (op_ce) |
r_op_Bv <= w_op_BnI + dcd_I; |
else if ((wr_reg_ce)&&(op_Bid == wr_reg_id)&&(op_rB)) |
r_op_Bv <= wr_gpreg_vl; |
`else |
if ((dcd_Bpc)&&(dcd_rB)) |
r_op_Bv <= w_pcB_v + { dcd_I[29:0], 2'b00 }; |
else |
r_op_Bv <= w_op_BnI + dcd_I; |
end else if ((OPT_PIPELINED)&&(op_rB) |
&&(wr_reg_ce)&&(op_Bid == wr_reg_id)) |
r_op_Bv <= wr_gpreg_vl; |
`endif |
|
// The logic here has become more complex than it should be, no thanks |
// to Xilinx's Vivado trying to help. The conditions are supposed to |
1007,27 → 767,25
// below, arriving at what we finally want in the (now wire net) |
// op_F. |
always @(posedge i_clk) |
if ((!OPT_PIPELINED)||(op_ce)) |
// Cannot do op_change_data_ce here since op_F depends |
// upon being either correct for a valid op, or correct |
// for the last valid op |
begin // Set the flag condition codes, bit order is [3:0]=VNCZ |
case(dcd_F[2:0]) |
3'h0: r_op_F <= 7'h00; // Always |
3'h1: r_op_F <= 7'h11; // Z |
3'h2: r_op_F <= 7'h44; // LT |
3'h3: r_op_F <= 7'h22; // C |
3'h4: r_op_F <= 7'h08; // V |
3'h5: r_op_F <= 7'h10; // NE |
3'h6: r_op_F <= 7'h40; // GE (!N) |
3'h7: r_op_F <= 7'h20; // NC |
endcase |
end // Bit order is { (flags_not_used), VNCZ mask, VNCZ value } |
if (op_ce) // Cannot do op_change_data_ce here since op_F depends |
// upon being either correct for a valid op, or correct |
// for the last valid op |
begin // Set the flag condition codes, bit order is [3:0]=VNCZ |
case(dcd_F[2:0]) |
3'h0: r_op_F <= 7'h00; // Always |
3'h1: r_op_F <= 7'h11; // Z |
3'h2: r_op_F <= 7'h44; // LT |
3'h3: r_op_F <= 7'h22; // C |
3'h4: r_op_F <= 7'h08; // V |
3'h5: r_op_F <= 7'h10; // NE |
3'h6: r_op_F <= 7'h40; // GE (!N) |
3'h7: r_op_F <= 7'h20; // NC |
endcase |
end // Bit order is { (flags_not_used), VNCZ mask, VNCZ value } |
assign op_F = { r_op_F[3], r_op_F[6:0] }; |
|
assign w_op_valid = (!clear_pipeline)&&(dcd_valid) |
&&(!dcd_ljmp)&&(!dcd_early_branch); |
|
wire w_op_valid; |
assign w_op_valid = (!clear_pipeline)&&(dcd_valid)&&(!dcd_ljmp)&&(!dcd_early_branch); |
initial op_valid = 1'b0; |
initial op_valid_alu = 1'b0; |
initial op_valid_mem = 1'b0; |
1034,31 → 792,29
initial op_valid_div = 1'b0; |
initial op_valid_fpu = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(clear_pipeline)) |
begin |
op_valid <= 1'b0; |
op_valid_alu <= 1'b0; |
op_valid_mem <= 1'b0; |
op_valid_div <= 1'b0; |
op_valid_fpu <= 1'b0; |
end else if (op_ce) |
begin |
// Do we have a valid instruction? |
// The decoder may vote to stall one of its |
// instructions based upon something we currently |
// have in our queue. This instruction must then |
// move forward, and get a stall cycle inserted. |
// Hence, the test on dcd_stalled here. If we must |
// wait until our operands are valid, then we aren't |
// valid yet until then. |
if (OPT_PIPELINED || !op_valid) |
if (clear_pipeline) |
begin |
op_valid <= (w_op_valid)||(dcd_early_branch); |
op_valid_alu <= (w_op_valid)&&((dcd_ALU)||(dcd_illegal)); |
op_valid_mem <= (dcd_M)&&(!dcd_illegal) |
&&(w_op_valid); |
op_valid_div <= (IMPLEMENT_DIVIDE)&&(dcd_DIV)&&(!dcd_illegal)&&(w_op_valid); |
op_valid_fpu <= (IMPLEMENT_FPU)&&(dcd_FP)&&(!dcd_illegal)&&(w_op_valid); |
op_valid <= 1'b0; |
op_valid_alu <= 1'b0; |
op_valid_mem <= 1'b0; |
op_valid_div <= 1'b0; |
op_valid_fpu <= 1'b0; |
end else if (op_ce) |
begin |
// Do we have a valid instruction? |
// The decoder may vote to stall one of its |
// instructions based upon something we currently |
// have in our queue. This instruction must then |
// move forward, and get a stall cycle inserted. |
// Hence, the test on dcd_stalled here. If we must |
// wait until our operands are valid, then we aren't |
// valid yet until then. |
op_valid<= (w_op_valid)||(dcd_illegal)&&(dcd_valid)||(dcd_early_branch); |
op_valid_alu <= (w_op_valid)&&((dcd_ALU)||(dcd_illegal) |
||(dcd_early_branch)); |
op_valid_mem <= (dcd_M)&&(!dcd_illegal)&&(w_op_valid); |
op_valid_div <= (dcd_DIV)&&(!dcd_illegal)&&(w_op_valid); |
op_valid_fpu <= (dcd_FP)&&(!dcd_illegal)&&(w_op_valid); |
end else if ((adf_ce_unconditional)||(mem_ce)) |
begin |
op_valid <= 1'b0; |
1067,14 → 823,6
op_valid_div <= 1'b0; |
op_valid_fpu <= 1'b0; |
end |
end else if ((adf_ce_unconditional)||(mem_ce)) |
begin |
op_valid <= 1'b0; |
op_valid_alu <= 1'b0; |
op_valid_mem <= 1'b0; |
op_valid_div <= 1'b0; |
op_valid_fpu <= 1'b0; |
end |
|
// Here's part of our debug interface. When we recognize a break |
// instruction, we set the op_break flag. That'll prevent this |
1085,76 → 833,74
// to be, step through it, and then replace it back. In this fashion, |
// a debugger can step through code. |
// assign w_op_break = (dcd_break)&&(r_dcd_I[15:0] == 16'h0001); |
reg r_op_break; |
|
initial r_op_break = 1'b0; |
always @(posedge i_clk) |
if (clear_pipeline) |
r_op_break <= 1'b0; |
else if ((OPT_PIPELINED)&&(op_ce)) |
r_op_break <= (dcd_valid)&&(dcd_break)&&(!dcd_illegal); |
else if ((!OPT_PIPELINED)&&(dcd_valid)) |
r_op_break <= (dcd_break)&&(!dcd_illegal); |
if ((i_rst)||(clear_pipeline)) r_op_break <= 1'b0; |
else if (op_ce) |
r_op_break <= (dcd_break); |
else if (!op_valid) |
r_op_break <= 1'b0; |
assign op_break = r_op_break; |
|
generate if ((!OPT_PIPELINED)||(!OPT_LOCK)) |
`ifdef OPT_PIPELINED |
generate |
if (IMPLEMENT_LOCK != 0) |
begin |
|
assign op_lock = 1'b0; |
|
// Verilator lint_off UNUSED |
wire dcd_lock_unused; |
assign dcd_lock_unused = dcd_lock; |
// Verilator lint_on UNUSED |
|
end else // if (IMPLEMENT_LOCK != 0) |
begin : OPLOCK |
reg r_op_lock; |
|
initial r_op_lock = 1'b0; |
always @(posedge i_clk) |
if (clear_pipeline) |
r_op_lock <= 1'b0; |
else if (op_ce) |
r_op_lock <= (dcd_valid)&&(dcd_lock) |
&&(!dcd_illegal); |
if (clear_pipeline) |
r_op_lock <= 1'b0; |
else if (op_ce) |
r_op_lock <= (dcd_valid)&&(dcd_lock)&&(!clear_pipeline); |
assign op_lock = r_op_lock; |
|
end else begin |
assign op_lock = 1'b0; |
end endgenerate |
|
`else |
assign op_lock = 1'b0; |
`endif |
|
`ifdef OPT_ILLEGAL_INSTRUCTION |
initial op_illegal = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(clear_pipeline)) |
op_illegal <= 1'b0; |
else if (OPT_PIPELINED) |
begin |
if (clear_pipeline) |
op_illegal <= 1'b0; |
else if(op_ce) |
`ifdef OPT_PIPELINED |
op_illegal <= (dcd_valid)&&((dcd_illegal)||((dcd_lock)&&(IMPLEMENT_LOCK == 0))); |
`else |
op_illegal <= (dcd_valid)&&((dcd_illegal)||(dcd_lock)); |
`endif |
else if(alu_ce) |
op_illegal <= 1'b0; |
`endif |
|
// No generate on EARLY_BRANCHING here, since if EARLY_BRANCHING is not |
// set, dcd_early_branch will simply be a wire connected to zero and |
// this logic should just optimize. |
`ifdef OPT_PIPELINED |
always @(posedge i_clk) |
if (op_ce) |
op_illegal <= (dcd_valid)&&(!dcd_ljmp) |
&&(!dcd_early_branch)&&(dcd_illegal); |
end else if (!OPT_PIPELINED) |
begin |
op_wF <= (dcd_wF)&&((!dcd_Rcc)||(!dcd_wR)) |
&&(!dcd_early_branch)&&(!dcd_illegal); |
op_wR <= (dcd_wR)&&(!dcd_early_branch)&&(!dcd_illegal); |
end |
`else |
always @(posedge i_clk) |
begin |
if (dcd_valid) |
op_illegal <= (!dcd_ljmp)&&(!dcd_early_branch)&&(dcd_illegal); |
op_wF <= (dcd_wF)&&((!dcd_Rcc)||(!dcd_wR)) |
&&(!dcd_early_branch)&&(!dcd_illegal); |
op_wR <= (dcd_wR)&&(!dcd_early_branch)&&(!dcd_illegal); |
end |
`endif |
|
always @(posedge i_clk) |
if ((!OPT_PIPELINED)||(op_ce)) |
op_wF <= (dcd_wF)&&((!dcd_Rcc)||(!dcd_wR)) |
&&(!dcd_early_branch); |
|
generate if ((OPT_PIPELINED)||(EARLY_BRANCHING)) |
begin |
|
always @(posedge i_clk) |
if (op_ce) |
op_wR <= (dcd_wR)&&(!dcd_early_branch); |
|
end else begin |
|
always @(*) |
op_wR = (dcd_wR); |
|
end endgenerate |
|
`ifdef VERILATOR |
`ifdef SINGLE_FETCH |
always @(*) |
1164,7 → 910,7
end |
`else |
always @(posedge i_clk) |
if (op_ce) |
if (op_change_data_ce) |
begin |
op_sim <= dcd_sim; |
op_sim_immv <= dcd_sim_immv; |
1172,68 → 918,48
`endif |
`endif |
|
reg [3:0] r_op_opn; |
reg [4:0] r_op_R; |
reg r_op_Rcc; |
reg r_op_gie; |
|
generate if ((OPT_PIPELINED)||(EARLY_BRANCHING)) |
begin : SET_OP_PC |
|
initial op_pc[0] = 1'b0; |
always @(posedge i_clk) |
if (op_ce) |
op_pc <= (dcd_early_branch)?dcd_branch_pc:dcd_pc; |
|
end else begin : SET_OP_PC |
|
always @(*) |
op_pc = dcd_pc; |
|
end endgenerate |
|
generate if (!OPT_PIPELINED) |
begin |
always @(*) |
r_op_opn = dcd_opn; |
|
end else begin |
|
always @(posedge i_clk) |
if (op_ce) |
initial r_op_gie = 1'b0; |
always @(posedge i_clk) |
if (op_change_data_ce) |
begin |
// Which ALU operation? Early branches are |
// unimplemented moves |
r_op_opn <= ((dcd_early_branch)||(dcd_illegal)) |
? `CPU_MOV_OP : dcd_opn; |
r_op_opn <= (dcd_early_branch) ? 4'hf : dcd_opn; |
// opM <= dcd_M; // Is this a memory operation? |
// What register will these results be written into? |
r_op_R <= dcd_R; |
r_op_Rcc <= (dcd_Rcc)&&(dcd_wR)&&(dcd_R[4]==dcd_gie); |
// User level (1), vs supervisor (0)/interrupts disabled |
r_op_gie <= dcd_gie; |
|
// |
op_pc <= (dcd_early_branch)?dcd_branch_pc:dcd_pc[AW:1]; |
end |
|
end endgenerate |
|
assign op_opn = r_op_opn; |
assign op_gie = gie; |
assign op_R = r_op_R; |
assign op_gie = r_op_gie; |
assign op_Rcc = r_op_Rcc; |
|
assign op_Fl = (op_gie)?(w_uflags[3:0]):(w_iflags[3:0]); |
assign op_Fl = (op_gie)?(w_uflags):(w_iflags); |
|
generate if (OPT_CIS) |
begin : OPT_CIS_OP_PHASE |
`ifdef OPT_CIS |
reg r_op_phase; |
initial r_op_phase = 1'b0; |
always @(posedge i_clk) |
if (clear_pipeline) |
r_op_phase <= 1'b0; |
else if (op_change_data_ce) |
r_op_phase <= (dcd_phase)&&((!dcd_wR)||(!dcd_Rpc)); |
assign op_phase = r_op_phase; |
`else |
assign op_phase = 1'b0; |
`endif |
|
reg r_op_phase; |
|
initial r_op_phase = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(clear_pipeline)) |
r_op_phase <= 1'b0; |
else if (op_ce) |
r_op_phase <= (dcd_phase)&&((!dcd_wR)||(!dcd_Rpc)); |
assign op_phase = r_op_phase; |
end else begin : OPT_NOCIS_OP_PHASE |
assign op_phase = 1'b0; |
|
// verilator lint_off UNUSED |
wire OPT_CIS_dcdRpc; |
assign OPT_CIS_dcdRpc = dcd_Rpc; |
// verilator lint_on UNUSED |
end endgenerate |
|
// This is tricky. First, the PC and Flags registers aren't kept in |
// register set but in special registers of their own. So step one |
// is to select the right register. Step to is to replace that |
1246,18 → 972,14
// We'll create a flag here to start our coordination. Once we |
// define this flag to something other than just plain zero, then |
// the stalls will already be in place. |
generate if (OPT_PIPELINED) |
begin |
|
assign op_Av = ((wr_reg_ce)&&(wr_reg_id == op_Aid)) |
`ifdef OPT_PIPELINED |
assign op_Av = ((wr_reg_ce)&&(wr_reg_id == op_Aid)) // &&(op_rA)) |
? wr_gpreg_vl : r_op_Av; |
`else |
assign op_Av = r_op_Av; |
`endif |
|
end else begin |
|
assign op_Av = r_op_Av; |
|
end endgenerate |
|
`ifdef OPT_PIPELINED |
// Stall if we have decoded an instruction that will read register A |
// AND ... something that may write a register is running |
// AND (series of conditions here ...) |
1264,27 → 986,24
// The operation might set flags, and we wish to read the |
// CC register |
// OR ... (No other conditions) |
generate if (OPT_PIPELINED) |
begin |
|
assign dcd_A_stall = (dcd_rA) // &&(dcd_valid) is checked for elsewhere |
assign dcd_A_stall = (dcd_rA) // &&(dcd_valid) is checked for elsewhere |
&&((op_valid)||(mem_rdbusy) |
||(div_busy)||(fpu_busy)) |
&&(((op_wF)||(cc_invalid_for_dcd))&&(dcd_Acc)) |
||((dcd_rA)&&(dcd_Acc)&&(cc_invalid_for_dcd)); |
end else begin |
`else |
// There are no pipeline hazards, if we aren't pipelined |
assign dcd_A_stall = 1'b0; |
`endif |
|
// There are no pipeline hazards, if we aren't pipelined |
assign dcd_A_stall = 1'b0; |
|
end endgenerate |
|
assign op_Bv = ((OPT_PIPELINED)&&(wr_reg_ce) |
&&(wr_reg_id == op_Bid)&&(op_rB)) |
`ifdef OPT_PIPELINED |
assign op_Bv = ((wr_reg_ce)&&(wr_reg_id == op_Bid)&&(op_rB)) |
? wr_gpreg_vl: r_op_Bv; |
`else |
assign op_Bv = r_op_Bv; |
`endif |
|
generate if (OPT_PIPELINED) |
begin |
`ifdef OPT_PIPELINED |
// Stall if we have decoded an instruction that will read register B |
// AND ... something that may write a (unknown) register is running |
// AND (series of conditions here ...) |
1293,7 → 1012,6
// OR the operation might set register B, and we still need |
// a clock to add the offset to it |
assign dcd_B_stall = (dcd_rB) // &&(dcd_valid) is checked for elsewhere |
//{{{ |
// If the op stage isn't valid, yet something |
// is running, then it must have been valid. |
// We'll use the last values from that stage |
1321,8 → 1039,6
((!dcd_zI)&&( |
((op_R == dcd_B)&&(op_wR)) |
||((mem_rdbusy)&&(!dcd_pipe)) |
||(((alu_busy)||(div_busy))&&(alu_reg == dcd_B)) |
||((wr_reg_ce)&&(wr_reg_id[3:1] == 3'h7)) |
)) |
// Stall following any instruction that will |
// set the flags, if we're going to need the |
1333,75 → 1049,45
// ||((mem_busy)&&(!mem_we)&&(mem_last_reg==dcd_B)&&(!dcd_zI)) |
) |
||((dcd_rB)&&(dcd_Bcc)&&(cc_invalid_for_dcd)); |
//}}} |
assign dcd_F_stall = ((!dcd_F[3]) |
//{{{ |
||((dcd_rA)&&(dcd_A[3:1]==3'h7) |
&&(dcd_A[4:0] != { gie, 4'hf})) |
||((dcd_rB)&&(dcd_B[3:1]==3'h7)) |
&&(dcd_B[4:0] != { gie, 4'hf})) |
&&(((op_valid)&&(op_wR) |
&&(op_R[3:1]==3'h7) |
&&(op_R[4:0]!={gie, 4'hf})) |
||(pending_sreg_write)); |
assign dcd_F_stall = ((!dcd_F[3]) |
||((dcd_rA)&&(dcd_Acc)) |
||((dcd_rB)&&(dcd_Bcc))) |
&&(op_valid)&&(op_Rcc); |
// &&(dcd_valid) is checked for elsewhere |
//}}} |
end else begin |
// No stalls without pipelining, 'cause how can you have a pipeline |
// hazard without the pipeline? |
assign dcd_B_stall = 1'b0; |
assign dcd_F_stall = 1'b0; |
end endgenerate |
|
//}}} |
`else |
// No stalls without pipelining, 'cause how can you have a pipeline |
// hazard without the pipeline? |
assign dcd_B_stall = 1'b0; |
assign dcd_F_stall = 1'b0; |
`endif |
// |
// |
// PIPELINE STAGE #4 :: Apply Instruction |
// |
// |
// ALU |
cpuops #(IMPLEMENT_MPY) doalu(i_clk, ((i_reset)||(clear_pipeline)), |
//{{{ |
cpuops #(IMPLEMENT_MPY) doalu(i_clk, (clear_pipeline), |
alu_ce, op_opn, op_Av, op_Bv, |
alu_result, alu_flags, alu_valid, alu_busy); |
//}}} |
|
// Divide |
//{{{ |
generate if (IMPLEMENT_DIVIDE != 0) |
begin : DIVIDE |
`ifdef FORMAL |
`define DIVIDE_MODULE abs_div |
`else |
`define DIVIDE_MODULE div |
`endif |
`DIVIDE_MODULE thedivide(i_clk, ((i_reset)||(clear_pipeline)), |
div_ce, op_opn[0], |
generate |
if (IMPLEMENT_DIVIDE != 0) |
begin |
div thedivide(i_clk, (clear_pipeline), div_ce, op_opn[0], |
op_Av, op_Bv, div_busy, div_valid, div_error, div_result, |
div_flags); |
|
end else begin |
|
assign div_error = 1'b0; // Can't be high unless div_valid |
assign div_busy = 1'b0; |
assign div_valid = 1'b0; |
assign div_result= 32'h00; |
assign div_flags = 4'h0; |
|
// Make verilator happy here |
// verilator lint_off UNUSED |
wire unused_divide; |
assign unused_divide = div_ce; |
// verilator lint_on UNUSED |
end endgenerate |
//}}} |
|
// (Non-existent) FPU |
//{{{ |
generate if (IMPLEMENT_FPU != 0) |
begin : FPU |
generate |
if (IMPLEMENT_FPU != 0) |
begin |
// |
// sfpu thefpu(i_clk, i_reset, fpu_ce, op_opn[2:0], |
// sfpu thefpu(i_clk, i_rst, fpu_ce, |
// op_Av, op_Bv, fpu_busy, fpu_valid, fpu_err, fpu_result, |
// fpu_flags); |
// |
1417,16 → 1103,13
assign fpu_result= 32'h00; |
assign fpu_flags = 4'h0; |
end endgenerate |
//}}} |
|
|
assign set_cond = ((op_F[7:4]&op_Fl[3:0])==op_F[3:0]); |
initial alu_wF = 1'b0; |
initial alu_wR = 1'b0; |
generate if (OPT_PIPELINED) |
begin |
always @(posedge i_clk) |
if (i_reset) |
always @(posedge i_clk) |
if (i_rst) |
begin |
alu_wR <= 1'b0; |
alu_wF <= 1'b0; |
1433,130 → 1116,90
end else if (alu_ce) |
begin |
// alu_reg <= op_R; |
alu_wR <= (op_wR)&&(set_cond)&&(!op_illegal); |
alu_wF <= (op_wF)&&(set_cond)&&(!op_illegal); |
alu_wR <= (op_wR)&&(set_cond); |
alu_wF <= (op_wF)&&(set_cond); |
end else if (!alu_busy) begin |
// These are strobe signals, so clear them if not |
// set for any particular clock |
alu_wR <= (r_halted)&&(i_dbg_we); |
alu_wR <= (i_halt)&&(i_dbg_we); |
alu_wF <= 1'b0; |
end |
end else begin |
|
always @(posedge i_clk) |
alu_wR <= (op_wR)&&(set_cond)&&(!op_illegal); |
always @(posedge i_clk) |
alu_wF <= (op_wF)&&(set_cond)&&(!op_illegal); |
`ifdef OPT_CIS |
reg r_alu_phase; |
initial r_alu_phase = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
r_alu_phase <= 1'b0; |
else if ((adf_ce_unconditional)||(mem_ce)) |
r_alu_phase <= op_phase; |
assign alu_phase = r_alu_phase; |
`else |
assign alu_phase = 1'b0; |
`endif |
|
end endgenerate |
|
generate if (OPT_CIS) |
begin : GEN_ALU_PHASE |
|
reg r_alu_phase; |
initial r_alu_phase = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(clear_pipeline)) |
r_alu_phase <= 1'b0; |
else if (((adf_ce_unconditional)||(mem_ce))&&(op_valid)) |
r_alu_phase <= op_phase; |
else if ((adf_ce_unconditional)||(mem_ce)) |
r_alu_phase <= 1'b0; |
assign alu_phase = r_alu_phase; |
end else begin |
|
assign alu_phase = 1'b0; |
end endgenerate |
|
generate if (OPT_PIPELINED) |
begin |
|
always @(posedge i_clk) |
`ifdef OPT_PIPELINED |
always @(posedge i_clk) |
if (adf_ce_unconditional) |
alu_reg <= op_R; |
else if ((r_halted)&&(i_dbg_we)) |
else if ((i_halt)&&(i_dbg_we)) |
alu_reg <= i_dbg_reg; |
`else |
always @(posedge i_clk) |
if ((i_halt)&&(i_dbg_we)) |
alu_reg <= i_dbg_reg; |
else |
alu_reg <= op_R; |
`endif |
|
end else begin |
|
always @(posedge i_clk) |
if ((r_halted)&&(i_dbg_we)) |
alu_reg <= i_dbg_reg; |
else |
alu_reg <= op_R; |
end endgenerate |
|
// |
// DEBUG Register write access starts here |
// |
//{{{ |
reg dbgv; |
initial dbgv = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
dbgv <= 0; |
else |
dbgv <= (i_dbg_we)&&(r_halted); |
|
dbgv <= (!i_rst)&&(i_halt)&&(i_dbg_we)&&(r_halted); |
reg [31:0] dbg_val; |
always @(posedge i_clk) |
dbg_val <= i_dbg_data; |
`ifdef OPT_NO_USERMODE |
assign alu_gie = 1'b0; |
`else |
`ifdef OPT_PIPELINED |
reg r_alu_gie; |
|
always @(posedge i_clk) |
if ((i_reset)||(clear_pipeline)) |
dbg_clear_pipe <= 0; |
else if ((i_dbg_we)&&(r_halted)) |
begin |
if (!OPT_PIPELINED) |
dbg_clear_pipe <= 1'b1; |
else if ((i_dbg_reg == op_Bid)&&(op_rB)) |
dbg_clear_pipe <= 1'b1; |
else if (i_dbg_reg[3:1] == 3'h7) |
dbg_clear_pipe <= 1'b1; |
else |
dbg_clear_pipe <= 1'b0; |
end else if ((!OPT_PIPELINED)&&(i_clear_pf_cache)) |
dbg_clear_pipe <= 1'b1; |
else |
dbg_clear_pipe <= 1'b0; |
if ((adf_ce_unconditional)||(mem_ce)) |
r_alu_gie <= op_gie; |
assign alu_gie = r_alu_gie; |
`else |
assign alu_gie = op_gie; |
`endif |
`endif |
|
assign alu_gie = gie; |
//}}} |
|
generate if (OPT_PIPELINED) |
begin : GEN_ALU_PC |
reg [(AW+1):0] r_alu_pc; |
initial r_alu_pc = 0; |
always @(posedge i_clk) |
if (i_reset) |
r_alu_pc <= 0; |
else if ((adf_ce_unconditional) |
||((master_ce)&&(op_valid_mem) |
&&(!clear_pipeline)&&(!mem_stalled))) |
`ifdef OPT_PIPELINED |
reg [(AW-1):0] r_alu_pc; |
always @(posedge i_clk) |
if ((adf_ce_unconditional) |
||((master_ce)&&(op_valid_mem)&&(!clear_pipeline) |
&&(!mem_stalled))) |
r_alu_pc <= op_pc; |
assign alu_pc = r_alu_pc; |
assign alu_pc = r_alu_pc; |
`else |
assign alu_pc = op_pc; |
`endif |
|
end else begin |
reg r_alu_illegal; |
initial r_alu_illegal = 0; |
always @(posedge i_clk) |
if (clear_pipeline) |
r_alu_illegal <= 1'b0; |
else if (alu_ce) |
r_alu_illegal <= op_illegal; |
else |
r_alu_illegal <= 1'b0; |
assign alu_illegal = (r_alu_illegal); |
|
assign alu_pc = op_pc; |
|
end endgenerate |
|
generate if (OPT_PIPELINED) |
begin : SET_ALU_ILLEGAL |
reg r_alu_illegal; |
|
initial r_alu_illegal = 0; |
always @(posedge i_clk) |
if (clear_pipeline) |
r_alu_illegal <= 1'b0; |
else if (adf_ce_unconditional) |
r_alu_illegal <= op_illegal; |
else |
r_alu_illegal <= 1'b0; |
|
assign alu_illegal = (r_alu_illegal); |
end else begin : SET_ALU_ILLEGAL |
assign alu_illegal = op_illegal; |
end endgenerate |
|
initial r_alu_pc_valid = 1'b0; |
initial mem_pc_valid = 1'b0; |
always @(posedge i_clk) |
1568,16 → 1211,16
r_alu_pc_valid <= 1'b0; |
assign alu_pc_valid = (r_alu_pc_valid)&&((!alu_busy)&&(!div_busy)&&(!fpu_busy)); |
always @(posedge i_clk) |
if (i_reset) |
if (i_rst) |
mem_pc_valid <= 1'b0; |
else |
mem_pc_valid <= (mem_ce); |
|
// Bus lock logic |
//{{{ |
wire bus_lock; |
`ifdef OPT_PIPELINED |
generate |
if ((OPT_PIPELINED)&&(!OPT_LOCK)) |
begin : BUSLOCK |
if (IMPLEMENT_LOCK != 0) |
begin |
reg r_prelock_stall; |
|
initial r_prelock_stall = 1'b0; |
1592,7 → 1235,6
assign prelock_stall = r_prelock_stall; |
|
reg r_prelock_primed; |
initial r_prelock_primed = 1'b0; |
always @(posedge i_clk) |
if (clear_pipeline) |
r_prelock_primed <= 1'b0; |
1618,51 → 1260,12
assign prelock_stall = 1'b0; |
assign bus_lock = 1'b0; |
end endgenerate |
//}}} |
|
// Memory interface |
//{{{ |
generate if (OPT_DCACHE) |
begin : MEM_DCACHE |
|
dcache #(.LGCACHELEN(OPT_LGDCACHE), .ADDRESS_WIDTH(AW), |
.LGNLINES(OPT_LGDCACHE-3), .OPT_LOCAL_BUS(WITH_LOCAL_BUS), |
.OPT_PIPE(OPT_MEMPIPE), |
.OPT_LOCK(OPT_LOCK) |
`ifdef FORMAL |
, .OPT_FIFO_DEPTH(2) |
, .F_LGDEPTH(F_LGDEPTH) |
`else |
assign bus_lock = 1'b0; |
`endif |
) docache(i_clk, i_reset, |
///{{{ |
(mem_ce)&&(set_cond), bus_lock, |
(op_opn[2:0]), op_Bv, op_Av, op_R, |
mem_busy, mem_pipe_stalled, |
mem_valid, bus_err, mem_wreg, mem_result, |
mem_cyc_gbl, mem_cyc_lcl, |
mem_stb_gbl, mem_stb_lcl, |
mem_we, mem_addr, mem_data, mem_sel, |
mem_ack, mem_stall, mem_err, i_wb_data |
`ifdef FORMAL |
, f_mem_nreqs, f_mem_nacks, f_mem_outstanding, f_mem_pc |
`endif |
// , o_dcache_debug |
); |
///}}} |
end else begin : NO_CACHE |
if (OPT_PIPELINED_BUS_ACCESS) |
begin : MEM |
|
pipemem #(.ADDRESS_WIDTH(AW), |
.IMPLEMENT_LOCK(OPT_LOCK), |
.WITH_LOCAL_BUS(WITH_LOCAL_BUS) |
`ifdef FORMAL |
, .OPT_MAXDEPTH(4'h3), |
.F_LGDEPTH(F_LGDEPTH) |
`endif |
) domem(i_clk,i_reset, |
///{{{ |
(mem_ce)&&(set_cond), bus_lock, |
`ifdef OPT_PIPELINED_BUS_ACCESS |
pipemem #(AW,IMPLEMENT_LOCK) domem(i_clk, i_rst,(mem_ce)&&(set_cond), bus_lock, |
(op_opn[2:0]), op_Bv, op_Av, op_R, |
mem_busy, mem_pipe_stalled, |
mem_valid, bus_err, mem_wreg, mem_result, |
1669,22 → 1272,10
mem_cyc_gbl, mem_cyc_lcl, |
mem_stb_gbl, mem_stb_lcl, |
mem_we, mem_addr, mem_data, mem_sel, |
mem_ack, mem_stall, mem_err, i_wb_data |
`ifdef FORMAL |
, f_mem_nreqs, f_mem_nacks, f_mem_outstanding, f_mem_pc |
`endif |
); |
//}}} |
end else begin : MEM |
mem_ack, mem_stall, mem_err, i_wb_data); |
|
memops #(.ADDRESS_WIDTH(AW), |
.IMPLEMENT_LOCK(OPT_LOCK), |
.WITH_LOCAL_BUS(WITH_LOCAL_BUS) |
`ifdef FORMAL |
, .F_LGDEPTH(F_LGDEPTH) |
`endif // F_LGDEPTH |
) domem(i_clk,i_reset, |
//{{{ |
`else // PIPELINED_BUS_ACCESS |
memops #(AW,IMPLEMENT_LOCK,WITH_LOCAL_BUS) domem(i_clk, i_rst, |
(mem_ce)&&(set_cond), bus_lock, |
(op_opn[2:0]), op_Bv, op_Av, op_R, |
mem_busy, |
1692,28 → 1283,14
mem_cyc_gbl, mem_cyc_lcl, |
mem_stb_gbl, mem_stb_lcl, |
mem_we, mem_addr, mem_data, mem_sel, |
mem_ack, mem_stall, mem_err, i_wb_data |
`ifdef FORMAL |
, f_mem_nreqs, f_mem_nacks, f_mem_outstanding |
`endif |
); |
`ifdef FORMAL |
assign f_mem_pc = 1'b0; |
`endif |
//}}} |
assign mem_pipe_stalled = 1'b0; |
end end endgenerate |
mem_ack, mem_stall, mem_err, i_wb_data); |
assign mem_pipe_stalled = 1'b0; |
`endif // PIPELINED_BUS_ACCESS |
assign mem_rdbusy = ((mem_busy)&&(!mem_we)); |
|
assign mem_rdbusy = (mem_busy)&&((!OPT_PIPELINED)||(!mem_we)); |
|
// Either the prefetch or the instruction gets the memory bus, but |
// never both. |
wbdblpriarb #(.DW(32),.AW(AW) |
`ifdef FORMAL |
,.F_LGDEPTH(F_LGDEPTH), .F_MAX_STALL(2), .F_MAX_ACK_DELAY(2) |
`endif // FORMAL |
) pformem(i_clk, i_reset, |
//{{{ |
wbdblpriarb #(32,AW) pformem(i_clk, i_rst, |
// Memory access to the arbiter, priority position |
mem_cyc_gbl, mem_cyc_lcl, mem_stb_gbl, mem_stb_lcl, |
mem_we, mem_addr, mem_data, mem_sel, |
1733,20 → 1310,10
// Common wires, in and out, of the arbiter |
o_wb_gbl_cyc, o_wb_lcl_cyc, o_wb_gbl_stb, o_wb_lcl_stb, |
o_wb_we, o_wb_addr, o_wb_data, o_wb_sel, |
i_wb_ack, i_wb_stall, i_wb_err |
`ifdef FORMAL |
,f_gbl_arb_nreqs, f_gbl_arb_nacks, f_gbl_arb_outstanding, |
f_lcl_arb_nreqs, f_lcl_arb_nacks, f_lcl_arb_outstanding, |
f_gbl_mem_nreqs, f_gbl_mem_nacks, f_gbl_mem_outstanding, |
f_lcl_mem_nreqs, f_lcl_mem_nacks, f_lcl_mem_outstanding, |
f_gbl_pf_nreqs, f_gbl_pf_nacks, f_gbl_pf_outstanding, |
f_lcl_pf_nreqs, f_lcl_pf_nacks, f_lcl_pf_outstanding |
`endif |
); |
//}}} |
//}}} |
i_wb_ack, i_wb_stall, i_wb_err); |
|
|
|
// |
// |
// |
1757,7 → 1324,6
// |
// PIPELINE STAGE #5 :: Write-back results |
// |
//{{{ |
// |
// This stage is not allowed to stall. If results are ready to be |
// written back, they are written back at all cost. Sleepy CPU's |
1775,23 → 1341,20
assign wr_reg_ce = (dbgv)||(mem_valid) |
||((!clear_pipeline)&&(!alu_illegal) |
&&(((alu_wR)&&(alu_valid)) |
||((div_valid)&&(!div_error)) |
||((fpu_valid)&&(!fpu_error)))); |
||(div_valid)||(fpu_valid))); |
// Which register shall be written? |
// COULD SIMPLIFY THIS: by adding three bits to these registers, |
// One or PC, one for CC, and one for GIE match |
// Note that the alu_reg is the register to write on a divide or |
// FPU operation. |
generate if (OPT_NO_USERMODE) |
begin |
assign wr_reg_id[3:0] = (mem_valid) |
? mem_wreg[3:0] : alu_reg[3:0]; |
`ifdef OPT_NO_USERMODE |
assign wr_reg_id[3:0] = (alu_wR|div_valid|fpu_valid) |
? alu_reg[3:0]:mem_wreg[3:0]; |
assign wr_reg_id[4] = 1'b0; |
`else |
assign wr_reg_id = (alu_wR|div_valid|fpu_valid)?alu_reg:mem_wreg; |
`endif |
|
assign wr_reg_id[4] = 1'b0; |
end else begin |
assign wr_reg_id = (mem_valid) ? mem_wreg : alu_reg; |
end endgenerate |
|
// Are we writing to the CC register? |
assign wr_write_cc = (wr_reg_id[3:0] == `CPU_CC_REG); |
assign wr_write_scc = (wr_reg_id[4:0] == {1'b0, `CPU_CC_REG}); |
1806,30 → 1369,19
:((dbgv) ? dbg_val : alu_result)); |
assign wr_spreg_vl = ((mem_valid) ? mem_result |
:((dbgv) ? dbg_val : alu_result)); |
always @(posedge i_clk) |
if (wr_reg_ce) |
`ifdef OPT_NO_USERMODE |
regset[wr_reg_id[3:0]] <= wr_gpreg_vl; |
`else |
regset[wr_reg_id] <= wr_gpreg_vl; |
`endif |
|
generate if (OPT_NO_USERMODE) |
begin : SET_REGISTERS |
|
always @(posedge i_clk) |
if (wr_reg_ce) |
regset[{1'b0,wr_reg_id[3:0]}] <= wr_gpreg_vl; |
|
end else begin : SET_REGISTERS |
|
always @(posedge i_clk) |
if (wr_reg_ce) |
regset[wr_reg_id] <= wr_gpreg_vl; |
|
end endgenerate |
|
|
// |
// Write back to the condition codes/flags register ... |
// When shall we write to our flags register? alu_wF already |
// includes the set condition ... |
assign wr_flags_ce = (alu_wF)&&((alu_valid) |
||(div_valid)||(fpu_valid)) |
&&(!clear_pipeline)&&(!alu_illegal); |
assign wr_flags_ce = ((alu_wF)||(div_valid)||(fpu_valid))&&(!clear_pipeline)&&(!alu_illegal); |
assign w_uflags = { 1'b0, uhalt_phase, ufpu_err_flag, |
udiv_err_flag, ubus_err_flag, trap, ill_err_u, |
ubreak, step, 1'b1, sleep, |
1874,35 → 1426,30
// condition has taken place. |
initial break_en = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(i_halt)) |
if ((i_rst)||(i_halt)) |
break_en <= 1'b0; |
else if ((wr_reg_ce)&&(wr_write_scc)) |
break_en <= wr_spreg_vl[`CPU_BREAK_BIT]; |
|
generate if (OPT_PIPELINED) |
begin : GEN_PENDING_BREAK |
reg r_break_pending; |
`ifdef OPT_PIPELINED |
reg r_break_pending; |
|
initial r_break_pending = 1'b0; |
always @(posedge i_clk) |
if ((clear_pipeline)||(!op_valid)) |
r_break_pending <= 1'b0; |
else if ((op_break)&&(!r_break_pending)) |
r_break_pending <= (!alu_busy)&&(!div_busy) |
&&(!fpu_busy)&&(!mem_busy) |
&&(!wr_reg_ce); |
// else |
// r_break_pending <= 1'b0; |
assign break_pending = r_break_pending; |
end else begin |
initial r_break_pending = 1'b0; |
always @(posedge i_clk) |
if ((clear_pipeline)||(!op_valid)) |
r_break_pending <= 1'b0; |
else if (op_break) |
r_break_pending <= (!alu_busy)&&(!div_busy)&&(!fpu_busy)&&(!mem_busy)&&(!wr_reg_ce); |
else |
r_break_pending <= 1'b0; |
assign break_pending = r_break_pending; |
`else |
assign break_pending = op_break; |
`endif |
|
assign break_pending = op_break; |
end endgenerate |
|
|
assign o_break = ((break_en)||(!op_gie))&&(break_pending) |
&&(!clear_pipeline) |
||(ill_err_i) |
||((!alu_gie)&&(bus_err)) |
||((!alu_gie)&&(div_error)) |
||((!alu_gie)&&(fpu_error)) |
1915,108 → 1462,80
// set the sleep bit and switch to supervisor mode in the same |
// instruction: users are not allowed to halt the CPU. |
initial sleep = 1'b0; |
generate if (OPT_NO_USERMODE) |
begin : GEN_NO_USERMODE_SLEEP |
reg r_sleep_is_halt; |
initial r_sleep_is_halt = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
r_sleep_is_halt <= 1'b0; |
else if ((wr_reg_ce)&&(wr_write_cc) |
&&(wr_spreg_vl[`CPU_SLEEP_BIT]) |
&&(!wr_spreg_vl[`CPU_GIE_BIT])) |
r_sleep_is_halt <= 1'b1; |
`ifdef OPT_NO_USERMODE |
reg r_sleep_is_halt; |
initial r_sleep_is_halt = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
r_sleep_is_halt <= 1'b0; |
else if ((wr_reg_ce)&&(wr_write_cc) |
&&(wr_spreg_vl[`CPU_SLEEP_BIT]) |
&&(!wr_spreg_vl[`CPU_GIE_BIT])) |
r_sleep_is_halt <= 1'b1; |
|
// Trying to switch to user mode, either via a WAIT or an RTU |
// instruction will cause the CPU to sleep until an interrupt, in |
// the NO-USERMODE build. |
always @(posedge i_clk) |
if ((i_reset)||((i_interrupt)&&(!r_sleep_is_halt))) |
sleep <= 1'b0; |
else if ((wr_reg_ce)&&(wr_write_cc) |
&&(wr_spreg_vl[`CPU_GIE_BIT])) |
sleep <= 1'b1; |
end else begin : GEN_SLEEP |
// Trying to switch to user mode, either via a WAIT or an RTU |
// instruction will cause the CPU to sleep until an interrupt, in |
// the NO-USERMODE build. |
always @(posedge i_clk) |
if ((i_rst)||((i_interrupt)&&(!r_sleep_is_halt))) |
sleep <= 1'b0; |
else if ((wr_reg_ce)&&(wr_write_cc) |
&&(wr_spreg_vl[`CPU_GIE_BIT])) |
sleep <= 1'b1; |
`else |
always @(posedge i_clk) |
if ((i_rst)||(w_switch_to_interrupt)) |
sleep <= 1'b0; |
else if ((wr_reg_ce)&&(wr_write_cc)&&(!alu_gie)) |
// In supervisor mode, we have no protections. The |
// supervisor can set the sleep bit however he wants. |
// Well ... not quite. Switching to user mode and |
// sleep mode shouold only be possible if the interrupt |
// flag isn't set. |
// Thus: if (i_interrupt)&&(wr_spreg_vl[GIE]) |
// don't set the sleep bit |
// otherwise however it would o.w. be set |
sleep <= (wr_spreg_vl[`CPU_SLEEP_BIT]) |
&&((!i_interrupt)||(!wr_spreg_vl[`CPU_GIE_BIT])); |
else if ((wr_reg_ce)&&(wr_write_cc)&&(wr_spreg_vl[`CPU_GIE_BIT])) |
// In user mode, however, you can only set the sleep |
// mode while remaining in user mode. You can't switch |
// to sleep mode *and* supervisor mode at the same |
// time, lest you halt the CPU. |
sleep <= wr_spreg_vl[`CPU_SLEEP_BIT]; |
`endif |
|
always @(posedge i_clk) |
if ((i_reset)||(w_switch_to_interrupt)) |
sleep <= 1'b0; |
else if ((wr_reg_ce)&&(wr_write_cc)&&(!alu_gie)) |
// In supervisor mode, we have no protections. |
// The supervisor can set the sleep bit however |
// he wants. Well ... not quite. Switching to |
// user mode and sleep mode shouold only be |
// possible if the interrupt flag isn't set. |
// Thus: if (i_interrupt) |
// &&(wr_spreg_vl[GIE]) |
// don't set the sleep bit |
// otherwise however it would o.w. be set |
sleep <= (wr_spreg_vl[`CPU_SLEEP_BIT]) |
&&((!i_interrupt) |
||(!wr_spreg_vl[`CPU_GIE_BIT])); |
else if ((wr_reg_ce)&&(wr_write_cc) |
&&(wr_spreg_vl[`CPU_GIE_BIT])) |
// In user mode, however, you can only set the |
// sleep mode while remaining in user mode. |
// You can't switch to sleep mode *and* |
// supervisor mode at the same time, lest you |
// halt the CPU. |
sleep <= wr_spreg_vl[`CPU_SLEEP_BIT]; |
end endgenerate |
|
always @(posedge i_clk) |
if (i_reset) |
if (i_rst) |
step <= 1'b0; |
else if ((wr_reg_ce)&&(!alu_gie)&&(wr_write_ucc)) |
step <= wr_spreg_vl[`CPU_STEP_BIT]; |
|
// The GIE register. Only interrupts can disable the interrupt register |
generate if (OPT_NO_USERMODE) |
begin |
|
assign w_switch_to_interrupt = 1'b0; |
assign w_release_from_interrupt = 1'b0; |
|
end else begin : GEN_PENDING_INTERRUPT |
reg r_pending_interrupt; |
|
always @(posedge i_clk) |
if (i_reset) |
r_pending_interrupt <= 1'b0; |
else if ((clear_pipeline)||(w_switch_to_interrupt)||(!gie)) |
r_pending_interrupt <= 1'b0; |
else if (i_interrupt) |
r_pending_interrupt <= 1'b1; |
else if (adf_ce_unconditional) |
begin |
if ((op_illegal)||(step)||(break_pending)) |
r_pending_interrupt <= 1'b1; |
end else if (break_pending) |
r_pending_interrupt <= 1'b1; |
else if ((mem_ce)&&(step)) |
r_pending_interrupt <= 1'b1; |
|
assign pending_interrupt = r_pending_interrupt; |
|
|
assign w_switch_to_interrupt = (gie)&&( |
`ifdef OPT_NO_USERMODE |
assign w_switch_to_interrupt = 1'b0; |
assign w_release_from_interrupt = 1'b0; |
`else |
assign w_switch_to_interrupt = (gie)&&( |
// On interrupt (obviously) |
((pending_interrupt) |
&&(!alu_phase)&&(!bus_lock)&&(!mem_busy)) |
// |
((i_interrupt)&&(!alu_phase)&&(!bus_lock)) |
// If we are stepping the CPU |
||(((alu_pc_valid)||(mem_pc_valid))&&(step)&&(!alu_phase)&&(!bus_lock)) |
// If we encounter a break instruction, if the break |
// enable isn't set. |
||((master_ce)&&(break_pending)&&(!break_en)) |
// On an illegal instruction |
||((alu_illegal)&&(!clear_pipeline)) |
// On division by zero. If the divide isn't |
// implemented, div_valid and div_error will be short |
// circuited and that logic will be bypassed |
||(div_error) |
// |
// Same thing on a floating point error. Note that |
// fpu_error must *never* be set unless fpu_valid is |
// also set as well, else this will fail. |
||(fpu_error) |
// |
// |
||(bus_err) |
// |
// If we write to the CC register |
||((wr_reg_ce)&&(!wr_spreg_vl[`CPU_GIE_BIT]) |
&&(wr_reg_id[4])&&(wr_write_cc)) |
2026,66 → 1545,60
&&(((wr_reg_ce)&&(wr_spreg_vl[`CPU_GIE_BIT]) |
&&(wr_write_scc)) |
); |
end endgenerate |
`endif |
|
generate if (OPT_NO_USERMODE) |
begin |
assign gie = 1'b0; |
end else begin : SET_GIE |
`ifdef OPT_NO_USERMODE |
assign gie = 1'b0; |
`else |
reg r_gie; |
|
reg r_gie; |
initial r_gie = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
r_gie <= 1'b0; |
else if (w_switch_to_interrupt) |
r_gie <= 1'b0; |
else if (w_release_from_interrupt) |
r_gie <= 1'b1; |
assign gie = r_gie; |
`endif |
|
initial r_gie = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
r_gie <= 1'b0; |
else if (w_switch_to_interrupt) |
r_gie <= 1'b0; |
else if (w_release_from_interrupt) |
r_gie <= 1'b1; |
assign gie = r_gie; |
end endgenerate |
`ifdef OPT_NO_USERMODE |
assign trap = 1'b0; |
assign ubreak = 1'b0; |
`else |
reg r_trap; |
|
generate if (OPT_NO_USERMODE) |
begin |
initial r_trap = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(w_release_from_interrupt)) |
r_trap <= 1'b0; |
else if ((alu_gie)&&(wr_reg_ce)&&(!wr_spreg_vl[`CPU_GIE_BIT]) |
&&(wr_write_ucc)) // &&(wr_reg_id[4]) implied |
r_trap <= 1'b1; |
else if ((wr_reg_ce)&&(wr_write_ucc)&&(!alu_gie)) |
r_trap <= (r_trap)&&(wr_spreg_vl[`CPU_TRAP_BIT]); |
|
assign trap = 1'b0; |
assign ubreak = 1'b0; |
reg r_ubreak; |
|
end else begin : SET_TRAP_N_UBREAK |
initial r_ubreak = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(w_release_from_interrupt)) |
r_ubreak <= 1'b0; |
else if ((op_gie)&&(break_pending)&&(w_switch_to_interrupt)) |
r_ubreak <= 1'b1; |
else if (((!alu_gie)||(dbgv))&&(wr_reg_ce)&&(wr_write_ucc)) |
r_ubreak <= (ubreak)&&(wr_spreg_vl[`CPU_BREAK_BIT]); |
|
reg r_trap; |
assign trap = r_trap; |
assign ubreak = r_ubreak; |
`endif |
|
initial r_trap = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(w_release_from_interrupt)) |
r_trap <= 1'b0; |
else if ((alu_gie)&&(wr_reg_ce)&&(!wr_spreg_vl[`CPU_GIE_BIT]) |
&&(wr_write_ucc)) // &&(wr_reg_id[4]) implied |
r_trap <= 1'b1; |
else if ((wr_reg_ce)&&(wr_write_ucc)&&(!alu_gie)) |
r_trap <= (r_trap)&&(wr_spreg_vl[`CPU_TRAP_BIT]); |
|
reg r_ubreak; |
|
initial r_ubreak = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(w_release_from_interrupt)) |
r_ubreak <= 1'b0; |
else if ((op_gie)&&(break_pending)&&(w_switch_to_interrupt)) |
r_ubreak <= 1'b1; |
else if (((!alu_gie)||(dbgv))&&(wr_reg_ce)&&(wr_write_ucc)) |
r_ubreak <= (ubreak)&&(wr_spreg_vl[`CPU_BREAK_BIT]); |
|
assign trap = r_trap; |
assign ubreak = r_ubreak; |
|
end endgenerate |
|
|
`ifdef OPT_ILLEGAL_INSTRUCTION |
initial ill_err_i = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
if (i_rst) |
ill_err_i <= 1'b0; |
// Only the debug interface can clear this bit |
else if ((dbgv)&&(wr_write_scc)) |
2093,37 → 1606,35
else if ((alu_illegal)&&(!alu_gie)&&(!clear_pipeline)) |
ill_err_i <= 1'b1; |
|
generate if (OPT_NO_USERMODE) |
begin |
`ifdef OPT_NO_USERMODE |
assign ill_err_u = 1'b0; |
`else |
reg r_ill_err_u; |
|
assign ill_err_u = 1'b0; |
initial r_ill_err_u = 1'b0; |
always @(posedge i_clk) |
// The bit is automatically cleared on release from interrupt |
// or reset |
if ((i_rst)||(w_release_from_interrupt)) |
r_ill_err_u <= 1'b0; |
// If the supervisor (or debugger) writes to this register, |
// clearing the bit, then clear it |
else if (((!alu_gie)||(dbgv))&&(wr_reg_ce)&&(wr_write_ucc)) |
r_ill_err_u <=((ill_err_u)&&(wr_spreg_vl[`CPU_ILL_BIT])); |
else if ((alu_illegal)&&(alu_gie)&&(!clear_pipeline)) |
r_ill_err_u <= 1'b1; |
|
end else begin : SET_USER_ILLEGAL_INSN |
|
reg r_ill_err_u; |
|
initial r_ill_err_u = 1'b0; |
always @(posedge i_clk) |
// The bit is automatically cleared on release from interrupt |
// or reset |
if ((i_reset)||(w_release_from_interrupt)) |
r_ill_err_u <= 1'b0; |
// If the supervisor (or debugger) writes to this |
// register, clearing the bit, then clear it |
else if (((!alu_gie)||(dbgv))&&(wr_reg_ce)&&(wr_write_ucc)) |
r_ill_err_u <=((ill_err_u)&&(wr_spreg_vl[`CPU_ILL_BIT])); |
else if ((alu_illegal)&&(alu_gie)&&(!clear_pipeline)) |
r_ill_err_u <= 1'b1; |
|
assign ill_err_u = r_ill_err_u; |
|
end endgenerate |
|
assign ill_err_u = r_ill_err_u; |
`endif |
`else |
assign ill_err_u = 1'b0; |
assign ill_err_i = 1'b0; |
`endif |
// Supervisor/interrupt bus error flag -- this will crash the CPU if |
// ever set. |
initial ibus_err_flag = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
if (i_rst) |
ibus_err_flag <= 1'b0; |
else if ((dbgv)&&(wr_write_scc)) |
ibus_err_flag <= (ibus_err_flag)&&(wr_spreg_vl[`CPU_BUSERR_BIT]); |
2131,29 → 1642,26
ibus_err_flag <= 1'b1; |
// User bus error flag -- if ever set, it will cause an interrupt to |
// supervisor mode. |
generate if (OPT_NO_USERMODE) |
begin |
`ifdef OPT_NO_USERMODE |
assign ubus_err_flag = 1'b0; |
`else |
reg r_ubus_err_flag; |
|
assign ubus_err_flag = 1'b0; |
initial r_ubus_err_flag = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(w_release_from_interrupt)) |
r_ubus_err_flag <= 1'b0; |
else if (((!alu_gie)||(dbgv))&&(wr_reg_ce)&&(wr_write_ucc)) |
r_ubus_err_flag <= (ubus_err_flag)&&(wr_spreg_vl[`CPU_BUSERR_BIT]); |
else if ((bus_err)&&(alu_gie)) |
r_ubus_err_flag <= 1'b1; |
|
end else begin : SET_USER_BUSERR |
assign ubus_err_flag = r_ubus_err_flag; |
`endif |
|
reg r_ubus_err_flag; |
|
initial r_ubus_err_flag = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(w_release_from_interrupt)) |
r_ubus_err_flag <= 1'b0; |
else if (((!alu_gie)||(dbgv))&&(wr_reg_ce)&&(wr_write_ucc)) |
r_ubus_err_flag <= (ubus_err_flag)&&(wr_spreg_vl[`CPU_BUSERR_BIT]); |
else if ((bus_err)&&(alu_gie)) |
r_ubus_err_flag <= 1'b1; |
|
assign ubus_err_flag = r_ubus_err_flag; |
end endgenerate |
|
generate if (IMPLEMENT_DIVIDE != 0) |
begin : DIVERR |
generate |
if (IMPLEMENT_DIVIDE != 0) |
begin |
reg r_idiv_err_flag, r_udiv_err_flag; |
|
// Supervisor/interrupt divide (by zero) error flag -- this will |
2161,7 → 1669,7
// to be able to tell if/why the CPU crashed. |
initial r_idiv_err_flag = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
if (i_rst) |
r_idiv_err_flag <= 1'b0; |
else if ((dbgv)&&(wr_write_scc)) |
r_idiv_err_flag <= (r_idiv_err_flag)&&(wr_spreg_vl[`CPU_DIVERR_BIT]); |
2169,39 → 1677,37
r_idiv_err_flag <= 1'b1; |
|
assign idiv_err_flag = r_idiv_err_flag; |
`ifdef OPT_NO_USERMODE |
assign udiv_err_flag = 1'b0; |
`else |
// User divide (by zero) error flag -- if ever set, it will |
// cause a sudden switch interrupt to supervisor mode. |
initial r_udiv_err_flag = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(w_release_from_interrupt)) |
r_udiv_err_flag <= 1'b0; |
else if (((!alu_gie)||(dbgv))&&(wr_reg_ce) |
&&(wr_write_ucc)) |
r_udiv_err_flag <= (r_udiv_err_flag)&&(wr_spreg_vl[`CPU_DIVERR_BIT]); |
else if ((div_error)&&(alu_gie)) |
r_udiv_err_flag <= 1'b1; |
|
if (OPT_NO_USERMODE) |
begin |
assign udiv_err_flag = 1'b0; |
end else begin |
|
// User divide (by zero) error flag -- if ever set, it will |
// cause a sudden switch interrupt to supervisor mode. |
initial r_udiv_err_flag = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(w_release_from_interrupt)) |
r_udiv_err_flag <= 1'b0; |
else if (((!alu_gie)||(dbgv))&&(wr_reg_ce) |
&&(wr_write_ucc)) |
r_udiv_err_flag <= (r_udiv_err_flag)&&(wr_spreg_vl[`CPU_DIVERR_BIT]); |
else if ((div_error)&&(alu_gie)) |
r_udiv_err_flag <= 1'b1; |
|
assign udiv_err_flag = r_udiv_err_flag; |
end |
assign udiv_err_flag = r_udiv_err_flag; |
`endif |
end else begin |
assign idiv_err_flag = 1'b0; |
assign udiv_err_flag = 1'b0; |
end endgenerate |
|
generate if (IMPLEMENT_FPU !=0) |
begin : FPUERR |
generate |
if (IMPLEMENT_FPU !=0) |
begin |
// Supervisor/interrupt floating point error flag -- this will |
// crash the CPU if ever set. |
reg r_ifpu_err_flag, r_ufpu_err_flag; |
initial r_ifpu_err_flag = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
if (i_rst) |
r_ifpu_err_flag <= 1'b0; |
else if ((dbgv)&&(wr_write_scc)) |
r_ifpu_err_flag <= (r_ifpu_err_flag)&&(wr_spreg_vl[`CPU_FPUERR_BIT]); |
2211,7 → 1717,7
// a sudden switch interrupt to supervisor mode. |
initial r_ufpu_err_flag = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)&&(w_release_from_interrupt)) |
if ((i_rst)&&(w_release_from_interrupt)) |
r_ufpu_err_flag <= 1'b0; |
else if (((!alu_gie)||(dbgv))&&(wr_reg_ce) |
&&(wr_write_ucc)) |
2226,47 → 1732,39
assign ufpu_err_flag = 1'b0; |
end endgenerate |
|
generate if (OPT_CIS) |
begin : GEN_IHALT_PHASE |
reg r_ihalt_phase; |
`ifdef OPT_CIS |
reg r_ihalt_phase; |
|
initial r_ihalt_phase = 0; |
always @(posedge i_clk) |
if (i_reset) |
r_ihalt_phase <= 1'b0; |
else if ((!alu_gie)&&(alu_pc_valid)&&(!clear_pipeline)) |
r_ihalt_phase <= alu_phase; |
initial r_ihalt_phase = 0; |
always @(posedge i_clk) |
if (i_rst) |
r_ihalt_phase <= 1'b0; |
else if ((!alu_gie)&&(alu_pc_valid)&&(!clear_pipeline)) |
r_ihalt_phase <= alu_phase; |
|
assign ihalt_phase = r_ihalt_phase; |
end else begin : GEN_IHALT_PHASE |
assign ihalt_phase = r_ihalt_phase; |
|
assign ihalt_phase = 1'b0; |
`ifdef OPT_NO_USERMODE |
assign uhalt_phase = 1'b0; |
`else |
reg r_uhalt_phase; |
|
end endgenerate |
|
generate if ((!OPT_CIS) || (OPT_NO_USERMODE)) |
begin : GEN_UHALT_PHASE |
|
assign uhalt_phase = 1'b0; |
|
end else begin : GEN_UHALT_PHASE |
|
reg r_uhalt_phase; |
|
initial r_uhalt_phase = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(w_release_from_interrupt)) |
initial r_uhalt_phase = 0; |
always @(posedge i_clk) |
if ((i_rst)||(w_release_from_interrupt)) |
r_uhalt_phase <= 1'b0; |
else if ((alu_gie)&&(alu_pc_valid)) |
r_uhalt_phase <= alu_phase; |
else if ((!alu_gie)&&(wr_reg_ce)&&(wr_write_pc) |
&&(wr_reg_id[4])) |
r_uhalt_phase <= wr_spreg_vl[1]; |
else if ((!alu_gie)&&(wr_reg_ce)&&(wr_write_ucc)) |
r_uhalt_phase <= wr_spreg_vl[`CPU_PHASE_BIT]; |
|
assign uhalt_phase = r_uhalt_phase; |
assign uhalt_phase = r_uhalt_phase; |
`endif |
`else |
assign ihalt_phase = 1'b0; |
assign uhalt_phase = 1'b0; |
`endif |
|
end endgenerate |
|
// |
// Write backs to the PC register, and general increments of it |
// We support two: upc and ipc. If the instruction is normal, |
2277,217 → 1775,134
// What happens when the pipeline has gie and !gie instructions within |
// it? Do we clear both? What if a gie instruction tries to clear |
// a non-gie instruction? |
generate if (OPT_NO_USERMODE) |
begin |
`ifdef OPT_NO_USERMODE |
assign upc = {(AW+2){1'b0}}; |
`else |
reg [(AW+1):0] r_upc; |
|
assign upc = {(AW+2){1'b0}}; |
|
end else begin : SET_USER_PC |
|
reg [(AW+1):0] r_upc; |
|
always @(posedge i_clk) |
if ((wr_reg_ce)&&(wr_reg_id[4])&&(wr_write_pc)) |
r_upc <= { wr_spreg_vl[(AW+1):2], 2'b00 }; |
else if ((alu_gie)&& |
(((alu_pc_valid)&&(!clear_pipeline)&&(!alu_illegal)) |
||(mem_pc_valid))) |
r_upc <= alu_pc; |
assign upc = r_upc; |
end endgenerate |
|
initial ipc = { RESET_BUS_ADDRESS, 2'b00 }; |
always @(posedge i_clk) |
if (i_reset) |
ipc <= { RESET_BUS_ADDRESS, 2'b00 }; |
else if ((wr_reg_ce)&&(!wr_reg_id[4])&&(wr_write_pc)) |
ipc <= { wr_spreg_vl[(AW+1):2], 2'b00 }; |
else if ((!alu_gie)&&(!alu_phase)&& |
(((alu_pc_valid)&&(!clear_pipeline)&&(!alu_illegal)) |
||(mem_pc_valid))) |
ipc <= alu_pc; |
if ((wr_reg_ce)&&(wr_reg_id[4])&&(wr_write_pc)) |
r_upc <= { wr_spreg_vl[(AW+1):2], 2'b00 }; |
else if ((alu_gie)&& |
(((alu_pc_valid)&&(!clear_pipeline)&&(!alu_illegal)) |
||(mem_pc_valid))) |
r_upc <= { alu_pc, 2'b00 }; |
assign upc = r_upc; |
`endif |
|
initial pf_pc = { RESET_BUS_ADDRESS, 2'b00 }; |
always @(posedge i_clk) |
if (i_reset) |
pf_pc <= { RESET_BUS_ADDRESS, 2'b00 }; |
else if ((dbg_clear_pipe)&&(wr_reg_ce)&&(wr_write_pc)) |
pf_pc <= { wr_spreg_vl[(AW+1):2], 2'b00 }; |
else if ((w_switch_to_interrupt) |
||((!gie)&&((w_clear_icache)||(dbg_clear_pipe)))) |
pf_pc <= { ipc[(AW+1):2], 2'b00 }; |
else if ((w_release_from_interrupt)||((gie)&&((w_clear_icache)||(dbg_clear_pipe)))) |
pf_pc <= { upc[(AW+1):2], 2'b00 }; |
else if ((wr_reg_ce)&&(wr_reg_id[4] == gie)&&(wr_write_pc)) |
pf_pc <= { wr_spreg_vl[(AW+1):2], 2'b00 }; |
else if ((dcd_early_branch_stb)&&(!clear_pipeline)) |
pf_pc <= { dcd_branch_pc[AW+1:2] + 1'b1, 2'b00 }; |
else if ((new_pc)||((!pf_stalled)&&(pf_valid))) |
pf_pc <= { pf_pc[(AW+1):2] + 1'b1, 2'b00 }; |
if (i_rst) |
ipc <= { RESET_BUS_ADDRESS, 2'b00 }; |
else if ((wr_reg_ce)&&(!wr_reg_id[4])&&(wr_write_pc)) |
ipc <= { wr_spreg_vl[(AW+1):2], 2'b00 }; |
else if ((!alu_gie)&&(!alu_phase)&& |
(((alu_pc_valid)&&(!clear_pipeline)&&(!alu_illegal)) |
||(mem_pc_valid))) |
ipc <= { alu_pc, 2'b00 }; |
|
initial last_write_to_cc = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
last_write_to_cc <= 1'b0; |
else |
last_write_to_cc <= (wr_reg_ce)&&(wr_write_cc); |
assign cc_write_hold = (wr_reg_ce)&&(wr_write_cc)||(last_write_to_cc); |
if (i_rst) |
pf_pc <= { RESET_BUS_ADDRESS, 2'b00 }; |
else if ((w_switch_to_interrupt)||((!gie)&&(w_clear_icache))) |
pf_pc <= { ipc[(AW+1):2], 2'b00 }; |
else if ((w_release_from_interrupt)||((gie)&&(w_clear_icache))) |
pf_pc <= { upc[(AW+1):2], 2'b00 }; |
else if ((wr_reg_ce)&&(wr_reg_id[4] == gie)&&(wr_write_pc)) |
pf_pc <= { wr_spreg_vl[(AW+1):2], 2'b00 }; |
else if ((dcd_early_branch)&&(!clear_pipeline)) |
pf_pc <= { dcd_branch_pc + 1'b1, 2'b00 }; |
else if ((new_pc)||((!pf_stalled)&&(pf_valid))) |
pf_pc <= { pf_pc[(AW+1):2] + {{(AW-1){1'b0}},1'b1}, 2'b00 }; |
|
// If we aren't pipelined, or equivalently if we have no cache, these |
// instructions will get quietly (or not so quietly) ignored by the |
// optimizer. |
reg r_clear_icache; |
initial r_clear_icache = 1'b1; |
always @(posedge i_clk) |
if (i_reset) |
r_clear_icache <= 1'b0; |
else if ((r_halted)&&(i_clear_pf_cache)) |
r_clear_icache <= 1'b1; |
else if ((wr_reg_ce)&&(wr_write_scc)) |
r_clear_icache <= wr_spreg_vl[`CPU_CLRCACHE_BIT]; |
else |
r_clear_icache <= 1'b0; |
if ((i_rst)||(i_clear_pf_cache)) |
r_clear_icache <= 1'b1; |
else if ((wr_reg_ce)&&(wr_write_scc)) |
r_clear_icache <= wr_spreg_vl[`CPU_CLRCACHE_BIT]; |
else |
r_clear_icache <= 1'b0; |
assign w_clear_icache = r_clear_icache; |
|
initial new_pc = 1'b1; |
always @(posedge i_clk) |
if ((i_reset)||(w_clear_icache)||(dbg_clear_pipe)) |
if ((i_rst)||(w_clear_icache)) |
new_pc <= 1'b1; |
else if (w_switch_to_interrupt) |
new_pc <= 1'b1; |
else if (w_release_from_interrupt) |
new_pc <= 1'b1; |
// else if ((wr_reg_ce)&&(wr_reg_id[4] == gie)&&(wr_write_pc)) |
// Can't check for *this* PC here, since a user PC might be |
// loaded in the pipeline and hence rewritten. Thus, while |
// I hate to do it, we'll need to clear the pipeline on any |
// PC write |
else if ((wr_reg_ce)&&(alu_gie == wr_reg_id[4])&&(wr_write_pc)) |
else if ((wr_reg_ce)&&(wr_reg_id[4] == gie)&&(wr_write_pc)) |
new_pc <= 1'b1; |
else |
new_pc <= 1'b0; |
|
// |
// The debug write-back interface |
//{{{ |
// The debug interface |
wire [31:0] w_debug_pc; |
generate if (OPT_NO_USERMODE) |
begin |
|
assign w_debug_pc[(AW+1):0] = { ipc, 2'b00 }; |
end else begin |
|
assign w_debug_pc[(AW+1):0] = { (i_dbg_reg[4]) |
`ifdef OPT_NO_USERMODE |
assign w_debug_pc[(AW+1):0] = { ipc, 2'b00 }; |
`else |
assign w_debug_pc[(AW+1):0] = { (i_dbg_reg[4]) |
? { upc[(AW+1):2], uhalt_phase, 1'b0 } |
: { ipc[(AW+1):2], ihalt_phase, 1'b0 } }; |
end endgenerate |
|
`endif |
generate |
if (AW<30) |
assign w_debug_pc[31:(AW+2)] = 0; |
endgenerate |
|
generate if (OPT_NO_USERMODE) |
begin : NO_USER_SETDBG |
|
always @(posedge i_clk) |
always @(posedge i_clk) |
begin |
`ifdef OPT_NO_USERMODE |
o_dbg_reg <= regset[i_dbg_reg[3:0]]; |
if (i_dbg_reg[3:0] == `CPU_PC_REG) |
o_dbg_reg <= w_debug_pc; |
else if (i_dbg_reg[3:0] == `CPU_CC_REG) |
begin |
o_dbg_reg <= regset[i_dbg_reg[3:0]]; |
if (i_dbg_reg[3:0] == `CPU_PC_REG) |
o_dbg_reg <= w_debug_pc; |
else if (i_dbg_reg[3:0] == `CPU_CC_REG) |
begin |
o_dbg_reg[14:0] <= w_iflags; |
o_dbg_reg[15] <= 1'b0; |
o_dbg_reg[31:23] <= w_cpu_info; |
o_dbg_reg[`CPU_GIE_BIT] <= gie; |
end |
o_dbg_reg[14:0] <= w_iflags; |
o_dbg_reg[15] <= 1'b0; |
o_dbg_reg[31:23] <= w_cpu_info; |
o_dbg_reg[`CPU_GIE_BIT] <= gie; |
end |
end else begin : SETDBG |
|
`ifdef NO_DISTRIBUTED_RAM |
reg [31:0] pre_dbg_reg; |
always @(posedge i_clk) |
pre_dbg_reg <= regset[i_dbg_reg]; |
|
always @(posedge i_clk) |
begin |
o_dbg_reg <= pre_dbg_reg; |
if (i_dbg_reg[3:0] == `CPU_PC_REG) |
o_dbg_reg <= w_debug_pc; |
else if (i_dbg_reg[3:0] == `CPU_CC_REG) |
begin |
o_dbg_reg[14:0] <= (i_dbg_reg[4]) |
? w_uflags : w_iflags; |
o_dbg_reg[15] <= 1'b0; |
o_dbg_reg[31:23] <= w_cpu_info; |
o_dbg_reg[`CPU_GIE_BIT] <= gie; |
end |
end |
|
`else |
always @(posedge i_clk) |
o_dbg_reg <= regset[i_dbg_reg]; |
if (i_dbg_reg[3:0] == `CPU_PC_REG) |
o_dbg_reg <= w_debug_pc; |
else if (i_dbg_reg[3:0] == `CPU_CC_REG) |
begin |
o_dbg_reg <= regset[i_dbg_reg]; |
if (i_dbg_reg[3:0] == `CPU_PC_REG) |
o_dbg_reg <= w_debug_pc; |
else if (i_dbg_reg[3:0] == `CPU_CC_REG) |
begin |
o_dbg_reg[14:0] <= (i_dbg_reg[4]) |
? w_uflags : w_iflags; |
o_dbg_reg[15] <= 1'b0; |
o_dbg_reg[31:23] <= w_cpu_info; |
o_dbg_reg[`CPU_GIE_BIT] <= gie; |
end |
o_dbg_reg[14:0] <= (i_dbg_reg[4])?w_uflags:w_iflags; |
o_dbg_reg[15] <= 1'b0; |
o_dbg_reg[31:23] <= w_cpu_info; |
o_dbg_reg[`CPU_GIE_BIT] <= gie; |
end |
`endif |
end |
|
end endgenerate |
|
always @(posedge i_clk) |
o_dbg_cc <= { o_break, bus_err, gie, sleep }; |
|
generate if (OPT_PIPELINED) |
begin |
always @(posedge i_clk) |
r_halted <= (i_halt)&&(!alu_phase)&&(!bus_lock)&&( |
// To be halted, any long lasting instruction |
// must be completed. |
(!pf_cyc)&&(!mem_busy)&&(!alu_busy) |
&&(!div_busy)&&(!fpu_busy) |
// Operations must either be valid, or illegal |
&&((op_valid)||(i_reset)||(dcd_illegal)) |
// Decode stage must be either valid, in reset, |
// or producing an illelgal instruction |
&&((dcd_valid)||(i_reset)||(pf_illegal))); |
end else begin |
|
always @(posedge i_clk) |
r_halted <= (i_halt)&&(!alu_phase) |
// To be halted, any long lasting instruction |
// must be completed. |
&&(!pf_cyc)&&(!mem_busy)&&(!alu_busy) |
&&(!div_busy)&&(!fpu_busy); |
end endgenerate |
`ifdef NO_DISTRIBUTED_RAM |
reg r_dbg_stall; |
initial r_dbg_stall = 1'b1; |
|
`ifdef OPT_PIPELINED |
always @(posedge i_clk) |
if (i_reset) |
r_dbg_stall <= 1'b1; |
else if (!r_halted) |
r_dbg_stall <= 1'b1; |
else |
r_dbg_stall <= (!i_dbg_we)||(!r_dbg_stall); |
|
assign o_dbg_stall = !r_halted; |
r_halted <= (i_halt)&&( |
// To be halted, any long lasting instruction must |
// be completed. |
(!pf_cyc)&&(!mem_busy)&&(!alu_busy) |
&&(!div_busy)&&(!fpu_busy) |
// Operations must either be valid, or illegal |
&&((op_valid)||(i_rst)||(dcd_illegal)) |
// Decode stage must be either valid, in reset, or ill |
&&((dcd_valid)||(i_rst)||(pf_illegal))); |
`else |
always @(posedge i_clk) |
r_halted <= (i_halt)&&((op_valid)||(i_rst)); |
`endif |
assign o_dbg_stall = !r_halted; |
`endif |
//}}} |
|
//}}} |
|
// |
// |
// Produce accounting outputs: Account for any CPU stalls, so we can |
2499,1884 → 1914,52
assign o_i_count = (alu_pc_valid)&&(!clear_pipeline); |
|
`ifdef DEBUG_SCOPE |
//{{{ |
|
reg debug_trigger; |
initial debug_trigger = 1'b0; |
always @(posedge i_clk) |
debug_trigger <= (!i_halt)&&(o_break); |
o_debug <= { |
/* |
o_break, i_wb_err, pf_pc[1:0], |
flags, |
pf_valid, dcd_valid, op_valid, alu_valid, mem_valid, |
op_ce, alu_ce, mem_ce, |
// |
master_ce, op_valid_alu, op_valid_mem, |
// |
alu_stall, mem_busy, op_pipe, mem_pipe_stalled, |
mem_we, |
// ((op_valid_alu)&&(alu_stall)) |
// ||((op_valid_mem)&&(~op_pipe)&&(mem_busy)) |
// ||((op_valid_mem)&&( op_pipe)&&(mem_pipe_stalled))); |
// op_Av[23:20], op_Av[3:0], |
gie, sleep, wr_reg_ce, wr_gpreg_vl[4:0] |
*/ |
/* |
i_rst, master_ce, (new_pc), |
((dcd_early_branch)&&(dcd_valid)), |
pf_valid, pf_illegal, |
op_ce, dcd_ce, dcd_valid, dcd_stalled, |
pf_cyc, pf_stb, pf_we, pf_ack, pf_stall, pf_err, |
pf_pc[7:0], pf_addr[7:0] |
*/ |
|
wire [31:0] debug_flags; |
assign debug_flags = { debug_trigger, 3'b101, |
master_ce, i_halt, o_break, sleep, |
gie, ibus_err_flag, trap, ill_err_i, |
w_clear_icache, pf_valid, pf_illegal, dcd_ce, |
dcd_valid, dcd_stalled, op_ce, op_valid, |
op_pipe, alu_ce, alu_busy, alu_wR, |
alu_illegal, alu_wF, mem_ce, mem_we, |
mem_busy, mem_pipe_stalled, (new_pc), (dcd_early_branch) }; |
i_wb_err, gie, alu_illegal, |
(new_pc)||((dcd_early_branch)&&(~clear_pipeline)), |
mem_busy, |
(mem_busy)?{ (o_wb_gbl_stb|o_wb_lcl_stb), o_wb_we, |
o_wb_addr[8:0] } |
: { pf_instruction[31:21] }, |
pf_valid, (pf_valid) ? alu_pc[14:0] |
:{ pf_cyc, pf_stb, pf_pc[14:2] } |
|
/* |
wire [25:0] bus_debug; |
assign bus_debug = { debug_trigger, |
mem_ce, mem_we, mem_busy, mem_pipe_stalled, |
o_wb_gbl_cyc, o_wb_gbl_stb, o_wb_lcl_cyc, o_wb_lcl_stb, |
o_wb_we, i_wb_ack, i_wb_stall, i_wb_err, |
pf_cyc, pf_stb, pf_ack, pf_stall, |
pf_err, |
mem_cyc_gbl, mem_stb_gbl, mem_cyc_lcl, mem_stb_lcl, |
mem_we, mem_ack, mem_stall, mem_err |
/* |
i_wb_err, gie, new_pc, dcd_early_branch, // 4 |
pf_valid, pf_cyc, pf_stb, pf_instruction_pc[0], // 4 |
pf_instruction[30:27], // 4 |
dcd_gie, mem_busy, o_wb_gbl_cyc, o_wb_gbl_stb, // 4 |
dcd_valid, |
((dcd_early_branch)&&(~clear_pipeline)) // 15 |
? dcd_branch_pc[14:0]:pf_pc[14:0] |
*/ |
}; |
*/ |
|
// Verilator lint_off UNUSED |
wire [27:0] dbg_pc, dbg_wb_addr; |
// Verilator lint_on UNUSED |
generate if (AW-1 < 27) |
begin |
assign dbg_pc[(AW-1):0] = pf_pc[(AW+1):2]; |
assign dbg_pc[27:AW] = 0; |
|
assign dbg_wb_addr[(AW-1):0] = o_wb_addr; |
assign dbg_wb_addr[27:AW] = 0; |
end else // if (AW-1 >= 27) |
begin |
assign dbg_pc[27:0] = pf_pc[29:2]; |
assign dbg_wb_addr = o_wb_addr; |
end endgenerate |
|
always @(posedge i_clk) |
begin |
if ((i_halt)||(!master_ce)||(debug_trigger)||(o_break)) |
o_debug <= debug_flags; |
else if ((mem_valid)||((!clear_pipeline)&&(!alu_illegal) |
&&(((alu_wR)&&(alu_valid)) |
||(div_valid)||(fpu_valid)))) |
o_debug <= { debug_trigger, 1'b0, wr_reg_id[3:0], wr_gpreg_vl[25:0]}; |
else if (clear_pipeline) |
o_debug <= { debug_trigger, 3'b100, dbg_pc }; |
else if ((o_wb_gbl_stb)|(o_wb_lcl_stb)) |
o_debug <= {debug_trigger, 2'b11, o_wb_gbl_stb, o_wb_we, |
(o_wb_we)?o_wb_data[26:0] : dbg_wb_addr[26:0] }; |
else |
o_debug <= debug_flags; |
// o_debug[25:0] <= bus_debug; |
end |
//}}} |
`endif |
|
// Make verilator happy |
//{{{ |
// verilator lint_off UNUSED |
wire [56:0] unused; |
assign unused = { pf_new_pc, |
fpu_ce, pf_data, wr_spreg_vl[1:0], |
ipc[1:0], upc[1:0], pf_pc[1:0], |
dcd_rA, dcd_pipe, dcd_zI, |
dcd_A_stall, dcd_B_stall, dcd_F_stall, |
op_Rcc, op_pipe, op_lock, mem_pipe_stalled, prelock_stall, |
dcd_F }; |
generate if (AW+2 < 32) |
begin |
wire [31:(AW+2)] generic_ignore; |
assign generic_ignore = wr_spreg_vl[31:(AW+2)]; |
end endgenerate |
// verilator lint_on UNUSED |
//}}} |
|
// Formal methods |
//{{{ |
`ifdef FORMAL |
// PHASE_ONE is defined by default if nothing else is defined |
// |
`ifdef ZIPCPU |
`define ASSUME assume |
`else |
`define ASSUME assert |
`endif |
`define ASSERT assert |
// |
// |
|
wire [1+4+15+6+4+13+AW+1+32+4+23-1:0] f_dcd_data; |
wire fc_op_prepipe; |
wire [6:0] fc_alu_Aid; |
wire fc_alu_wR, fc_alu_M, fc_alu_prepipe; |
reg f_alu_phase; |
//////////////////////////////////////////////////////////////// |
// |
// |
// Formal methods section |
// |
// |
//////////////////////////////////////////////////////////////// |
reg f_past_valid; |
initial f_past_valid = 1'b0; |
always @(posedge i_clk) |
f_past_valid <= 1'b1; |
|
initial assume(i_reset); |
initial assume(!i_wb_ack); |
initial assume(!i_wb_err); |
always @(posedge i_clk) |
if (!f_past_valid) |
begin |
assume(i_reset); |
assume(!i_wb_ack); |
assume(!i_wb_err); |
end |
|
////////////////////////////////////////////// |
// |
// |
// The debugging interface |
// |
// |
////////////////////////////////////////////// |
// |
// |
|
// Reading from the debugging interface |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_halt))&&(!$past(i_dbg_we))) |
begin |
`ifdef NO_DISTRIBUTED_RAM |
if ($past(i_dbg_reg[3:1],2) != 3'h7) |
assert(o_dbg_reg |
== regset[i_dbg_reg[$past(i_dbg_reg,2)]]); |
`else |
if ($past(i_dbg_reg[3:1]) != 3'h7) |
assert(o_dbg_reg == regset[i_dbg_reg[$past(i_dbg_reg)]]); |
`endif |
if ($past(i_dbg_reg[4:0]) == 5'h0f) |
assert(o_dbg_reg[AW+1:0] == { ipc[(AW+1):2], ihalt_phase, 1'b0}); |
if ($past(i_dbg_reg[4:0]) == 5'h1f) |
assert(o_dbg_reg[AW+1:0] == { upc[(AW+1):2], uhalt_phase, 1'b0}); |
if ($past(i_dbg_reg[4:0]) == 5'h0e) |
begin |
assert(o_dbg_reg[14:6] == w_iflags[14:6]); |
assert(o_dbg_reg[ 4:0] == w_iflags[ 4:0]); |
end |
|
if ($past(i_dbg_reg[4:0]) == 5'h1e) |
begin |
assert(o_dbg_reg[14:6] == w_uflags[14:6]); |
assert(o_dbg_reg[ 4:0] == w_uflags[ 4:0]); |
end |
|
if ($past(i_dbg_reg[3:0]) == 4'he) |
begin |
assert(o_dbg_reg[15] == 1'b0); |
assert(o_dbg_reg[31:23] == w_cpu_info); |
assert(o_dbg_reg[`CPU_GIE_BIT] == gie); |
end |
end |
|
reg [2:0] f_dbg_pc_seq, f_dbg_cc_seq, f_dbg_reg_seq; |
initial f_dbg_pc_seq = 0; |
always @(posedge i_clk) |
if (i_reset) |
f_dbg_pc_seq <= 0; |
else begin |
f_dbg_pc_seq[0] <= r_halted && i_dbg_we |
&& (i_dbg_reg == { gie, `CPU_PC_REG }); |
f_dbg_pc_seq[2:1] <= f_dbg_pc_seq[1:0]; |
end |
|
always @(posedge i_clk) |
begin |
if (f_dbg_pc_seq[0]) |
assert(dbgv && alu_reg == { gie, `CPU_PC_REG }); |
if (f_dbg_pc_seq[1]) |
begin |
assert(clear_pipeline); |
assert(pf_request_address == $past(i_dbg_data,2)); |
end |
end |
|
initial f_dbg_cc_seq = 0; |
always @(posedge i_clk) |
if (i_reset) |
f_dbg_cc_seq <= 0; |
else begin |
f_dbg_cc_seq[0] <= r_halted && i_dbg_we |
&& (i_dbg_reg == { gie, `CPU_CC_REG }); |
f_dbg_cc_seq[2:1] <= f_dbg_cc_seq[1:0]; |
end |
|
always @(posedge i_clk) |
begin |
if (f_dbg_cc_seq[1]) |
begin |
assert(wr_reg_ce); |
assert(wr_reg_id == $past(i_dbg_reg,2)); |
assert(wr_spreg_vl == $past(i_dbg_data)); |
end |
end |
|
initial f_dbg_reg_seq = 0; |
always @(posedge i_clk) |
if (i_reset) |
f_dbg_reg_seq <= 0; |
else begin |
f_dbg_reg_seq[0] <= r_halted && i_dbg_we |
&& (i_dbg_reg[3:1] != 3'h7 ); |
f_dbg_reg_seq[2:1] <= f_dbg_reg_seq[1:0]; |
end |
|
always @(posedge i_clk) |
begin |
if (f_dbg_reg_seq[0]) |
begin |
assert(dbgv && alu_reg == $past(i_dbg_reg)); |
assert($past(i_dbg_reg[3:1]) != 3'h7); |
assert(dbg_val == $past(i_dbg_data)); |
end |
|
|
if (f_dbg_reg_seq[1]) |
begin |
assert(wr_reg_ce); |
assert(wr_gpreg_vl == $past(i_dbg_data,2)); |
assert(wr_reg_id == $past(i_dbg_reg,2)); |
end |
end |
|
/* |
`ifdef NO_DISTRIBUTED_RAM |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(f_past_valid))) |
assert(o_dbg_ack == $past(i_dbg_stb,2)); |
`else // NO_DISTRIBUTED_RAM |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(f_past_valid))) |
assert(o_dbg_ack == $past(i_dbg_stb)); |
`endif |
*/ |
|
////////////////////////////////////////////// |
// |
// |
// Problem limiting assumptions |
// |
// |
////////////////////////////////////////////// |
// |
// Take care that the assumptions below are actually representative |
// of how the CPU is used. One "careless" assumption could render |
// the proof meaningless. |
// |
// Because of the consequences of a careless assumption, we'll work |
// to place all of our assumptions at the beginning of the formal |
// properties for any phase. |
// |
|
// If the CPU is not halted, the debug interface will not try writing |
// to the CPU, nor will it try to issue a clear i-cache command |
always @(*) |
if (!r_halted) |
begin |
assume(!i_dbg_we); |
assume(!i_clear_pf_cache); |
end |
|
// A debug write will only take place during a CPUI halt |
always @(posedge i_clk) |
if (i_dbg_we) |
assume(i_halt); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_dbg_we))&&(!$past(o_dbg_stall))) |
assume(i_halt); |
|
always @(*) |
if ((!r_halted)||(!i_halt)) |
assume(!i_clear_pf_cache); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_clear_pf_cache))) |
assume(i_halt); |
|
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_dbg_we))&&($past(o_dbg_stall))) |
assume(($stable(i_dbg_we))&&($stable(i_dbg_data))); |
|
|
// Any attempt to set the PC will leave the bottom two bits clear |
always @(*) |
if ((wr_reg_ce)&&(wr_reg_id[3:0] == `CPU_PC_REG)) |
assume(wr_gpreg_vl[1:0] == 2'b00); |
|
// Once a halt is requested, the halt request will remain active until |
// the CPU comes to a complete halt. |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_halt))&&(!$past(r_halted))) |
assume(i_halt); |
|
// Once asserted, an interrupt will stay asserted while the CPU is |
// in user mode |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_interrupt))) |
begin |
if ($past(gie)) |
assume(i_interrupt); |
end |
|
always @(*) |
assume(!i_dbg_we); |
|
//////////////////////////////////////////////////////////////// |
// |
// |
// Reset checks |
// |
// |
//////////////////////////////////////////////////////////////// |
// |
// |
|
|
always @(posedge i_clk) |
if ((!f_past_valid)||($past(i_reset))) |
begin |
// Initial assertions |
`ASSERT(!pf_valid); |
`ASSERT(!dcd_phase); |
`ASSERT(!op_phase); |
`ASSERT(!alu_phase); |
// |
`ASSERT(!pf_valid); |
`ASSERT(!dcd_valid); |
`ASSERT(!op_valid); |
`ASSERT(!op_valid_mem); |
`ASSERT(!op_valid_div); |
`ASSERT(!op_valid_alu); |
`ASSERT(!op_valid_fpu); |
// |
`ASSERT(!alu_valid); |
`ASSERT(!alu_busy); |
// |
`ASSERT(!mem_valid); |
`ASSERT(!mem_busy); |
`ASSERT(!bus_err); |
// |
`ASSERT(!div_valid); |
`ASSERT(!div_busy); |
`ASSERT(!div_error); |
// |
`ASSERT(!fpu_valid); |
`ASSERT(!fpu_busy); |
`ASSERT(!fpu_error); |
// |
`ASSERT(!ill_err_i); |
`ASSERT(!ill_err_u); |
`ASSERT(!idiv_err_flag); |
`ASSERT(!udiv_err_flag); |
`ASSERT(!ibus_err_flag); |
`ASSERT(!ubus_err_flag); |
`ASSERT(!ifpu_err_flag); |
`ASSERT(!ufpu_err_flag); |
`ASSERT(!ihalt_phase); |
`ASSERT(!uhalt_phase); |
end |
|
always @(*) |
begin |
if (pf_valid) `ASSERT(f_past_valid); |
if (dcd_valid) `ASSERT(f_past_valid); |
if (alu_pc_valid) `ASSERT(f_past_valid); |
if (mem_valid) `ASSERT(f_past_valid); |
if (div_valid) `ASSERT(f_past_valid); |
if (fpu_valid) `ASSERT(f_past_valid); |
if (w_op_valid) `ASSERT(f_past_valid); |
if (mem_busy) `ASSERT(f_past_valid); |
if (mem_rdbusy) `ASSERT(f_past_valid); |
if (div_busy) `ASSERT(f_past_valid); |
if (fpu_busy) `ASSERT(f_past_valid); |
end |
|
//////////////////////////////////////////////////////////////// |
// |
// |
// Pipeline signaling check |
// |
// |
//////////////////////////////////////////////////////////////// |
// |
// |
|
always @(posedge i_clk) |
if (clear_pipeline) |
begin |
// `ASSERT(!alu_ce); |
`ASSERT(!mem_ce); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(clear_pipeline))) |
begin |
`ASSERT(!alu_busy); |
`ASSERT(!div_busy); |
`ASSERT(!mem_busy); |
`ASSERT(!fpu_busy); |
// |
`ASSERT(!alu_valid); |
`ASSERT(!div_valid); |
`ASSERT(!fpu_valid); |
end |
|
always @(*) |
if (dcd_ce) |
`ASSERT((op_ce)||(!dcd_valid)); |
|
always @(*) |
if ((op_ce)&&(!clear_pipeline)) |
`ASSERT((adf_ce_unconditional)||(mem_ce)||(!op_valid)); |
|
// |
// Make sure the dcd stage is never permanently stalled |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(alu_wR))&&(!$past(alu_wF)) |
&&($past(f_past_valid,2))&&(!$past(alu_wR,2))&&(!$past(alu_wF)) |
&&(!op_valid)&&(master_ce) |
&&(!clear_pipeline)&&(!i_reset) |
&&(!div_busy)&&(!div_valid) |
&&(!mem_busy)&&(!mem_valid)&&(!bus_err) |
&&(!alu_busy)&&(!alu_pc_valid)&&(!alu_valid) |
&&(!fpu_busy)&&(!fpu_valid)&&(!fpu_error) |
&&(!op_break)&&(!o_break) |
&&(!w_switch_to_interrupt) |
&&(!ibus_err_flag)&&(!ill_err_i)&&(!idiv_err_flag)) |
begin |
if (OPT_PIPELINED) |
assert(dcd_ce); |
if (!dcd_valid) |
assert(dcd_ce); |
end |
|
// |
// Make sure the ops stage is never permanently stalled |
always @(*) |
if ((op_valid)&&(master_ce)&&(!clear_pipeline)&&(!i_reset) |
&&(!div_busy)&&(!div_valid) |
&&(!mem_busy)&&(!mem_valid)&&(!bus_err) |
&&(!alu_busy)&&(!alu_pc_valid) |
&&(!fpu_busy)&&(!fpu_valid)&&(!fpu_error) |
&&(!op_break)&&(!o_break) |
&&(!w_switch_to_interrupt) |
&&(!alu_illegal) |
&&(!ibus_err_flag)&&(!ill_err_i)&&(!idiv_err_flag)) |
`ASSERT(adf_ce_unconditional | mem_ce); |
|
// |
// Make sure that, following an op_ce && op_valid, op_valid is only |
// true if dcd_valid was as well |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(op_ce && op_valid && !dcd_valid))) |
begin |
if ($past(dcd_early_branch)) |
`ASSERT(!dcd_early_branch); |
else |
`ASSERT(!op_valid); |
end |
|
// |
// Same for the next step |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(op_valid && (mem_ce ||adf_ce_unconditional))) |
&&(!$past(dcd_valid))) |
begin |
if ($past(dcd_early_branch)) |
`ASSERT(!dcd_early_branch); |
else |
`ASSERT(!op_valid); |
end |
|
//////////////////////////////////////////////// |
// |
// Assertions about the Program counter |
// |
//////////////////////////////////////////////// |
always @(*) |
`ASSERT(pf_instruction_pc[1:0]==2'b00); |
|
always @(*) |
if ((dcd_valid)&&(!dcd_illegal)) |
`ASSERT((!dcd_pc[1])||(dcd_phase)); |
|
always @(*) |
`ASSERT(!op_pc[0]); |
|
always @(*) |
`ASSERT(!alu_pc[0]); |
|
|
//////////////////////////////////////////////// |
// |
// Assertions about the prefetch (output) stage |
// |
//////////////////////////////////////////////// |
always @(posedge i_clk) |
if ((!clear_pipeline)&&(pf_valid)) |
`ASSERT(pf_gie == gie); |
|
always @(*) |
if ((pf_valid)&&(!clear_pipeline)) |
`ASSERT(pf_gie == gie); |
|
//////////////////////////////////////////////// |
// |
// Assertions about the decode stage |
// |
//////////////////////////////////////////////// |
// |
// |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&(!$past(clear_pipeline)) |
&&(!$past(w_clear_icache)) |
&&($past(dcd_valid))&&($past(dcd_stalled)) |
&&(!clear_pipeline)) |
begin |
`ASSERT((!OPT_PIPELINED)||(dcd_valid)); |
`ASSERT($stable(f_dcd_data)); |
`ASSERT($stable(f_dcd_insn_word)); |
end |
|
always @(*) |
if ((dcd_valid)&&(!clear_pipeline)) |
assert(f_dcd_insn_gie == dcd_gie); |
|
|
always @(posedge i_clk) |
if ((dcd_valid)&&(!dcd_illegal)&&(!clear_pipeline)) |
begin |
`ASSERT(dcd_gie == gie); |
if ((gie)||(dcd_phase)) |
begin |
`ASSERT((!dcd_wR)||(dcd_R[4]==dcd_gie)); |
`ASSERT((!dcd_rA)||(dcd_A[4]==dcd_gie)); |
`ASSERT((!dcd_rB)||(dcd_B[4]==dcd_gie)); |
end else if ((!dcd_early_branch)&&((dcd_M) |
||(dcd_DIV)||(dcd_FP)||(!dcd_wR))) |
`ASSERT(!dcd_gie); |
if ((dcd_ALU)&&(dcd_opn==`CPU_MOV_OP)) |
`ASSERT(((!dcd_rA)&&(dcd_wR)) |
||((!dcd_rA)&&(!dcd_rB)&&(!dcd_wR))); |
else if (dcd_ALU) |
`ASSERT( |
(gie == dcd_R[4]) |
&&(gie == dcd_A[4]) |
&&((!dcd_rB)||(gie == dcd_B[4])) |
&&(dcd_gie == gie)); |
end |
|
always @(*) |
if ((op_valid)&&(op_rA)&&(op_Aid[3:1] == 3'h7)&&(!clear_pipeline) |
&&(op_Aid[4:0] != { gie, 4'hf})) |
`ASSERT(!pending_sreg_write); |
always @(*) |
if ((op_valid)&&(op_rB)&&(op_Bid[3:1] == 3'h7)&&(!clear_pipeline) |
&&(op_Bid[4:0] != { gie, 4'hf})) |
`ASSERT(!pending_sreg_write); |
|
|
always @(*) |
if ((dcd_valid)&&(!clear_pipeline)) |
`ASSERT(dcd_gie == gie); |
|
// |
// |
// Piped Memory assertions |
// |
// |
always @(*) |
if ((dcd_valid)&&(dcd_M)&&(dcd_pipe)&&(!dcd_illegal)&&(!alu_illegal) |
&&(!break_pending)&&(!clear_pipeline)) |
begin |
if (op_valid_mem) |
begin |
`ASSERT(op_opn[0] == dcd_opn[0]); |
`ASSERT((!dcd_rB) |
||(op_Bid[4:0] == dcd_B[4:0])); |
`ASSERT(op_rB == dcd_rB); |
end |
`ASSERT(dcd_B[4] == dcd_gie); |
end |
|
always @(*) |
if ((op_valid_mem)&&(op_pipe)&&(mem_busy)&&(!mem_rdbusy)) |
`ASSERT(op_opn[0] == 1'b1); |
|
always @(*) |
if ((dcd_valid)&&(!dcd_M)) |
`ASSERT((dcd_illegal)||(!dcd_pipe)); |
|
wire [31:0] f_dcd_mem_addr, f_pipe_addr_diff; |
assign f_dcd_mem_addr = w_op_BnI+dcd_I; |
assign f_pipe_addr_diff = f_dcd_mem_addr - op_Bv; |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(dcd_early_branch))&&(!dcd_early_branch) |
&&(dcd_valid)) |
`ASSERT(!dcd_pipe); |
always @(*) |
if ((dcd_valid)&&(dcd_early_branch)) |
`ASSERT(!dcd_M); |
|
always @(*) |
if ((dcd_valid)&&(!dcd_illegal)&&(!fc_op_prepipe)) |
`ASSERT(!dcd_pipe); |
|
always @(*) |
if ((dcd_valid)&&(dcd_pipe)&&(w_op_valid)) |
begin |
// `ASSERT((dcd_A[3:1] != 3'h7)||(dcd_opn[0])); |
`ASSERT(dcd_B[3:1] != 3'h7); |
`ASSERT(dcd_rB); |
`ASSERT(dcd_M); |
`ASSERT(dcd_B == op_Bid); |
if (op_valid) |
`ASSERT((op_valid_mem)||(op_illegal)); |
if (((op_valid_mem)||(mem_busy))&&(dcd_rB) |
&&(!op_illegal)&&(!dcd_illegal) |
&&(!dbg_clear_pipe)&&(!clear_pipeline)) |
`ASSERT(f_pipe_addr_diff[AW+1:0] <= 4); |
if (op_valid_mem) |
begin |
`ASSERT((dcd_I[AW+1:3] == 0) |
||(!alu_busy)||(!div_busy) |
||(!alu_wR)||(alu_reg != dcd_B)); |
`ASSERT((!op_wR)||(op_Aid != op_Bid)); |
end |
end |
|
// |
// Decode option processing |
// |
|
// OPT_CIS ... the compressed instruction set |
always @(*) |
if ((!OPT_CIS)&&(dcd_valid)) |
begin |
`ASSERT(!dcd_phase); |
`ASSERT(dcd_pc[1:0] == 2'b0); |
end |
|
always @(*) |
if ((dcd_valid)&&(dcd_phase)) |
`ASSERT(f_dcd_insn_word[31]); |
|
|
// EARLY_BRANCHING |
always @(*) |
if (!EARLY_BRANCHING) |
`ASSERT((!dcd_early_branch) |
&&(!dcd_early_branch_stb) |
&&(!dcd_ljmp)); |
|
// IMPLEMENT_DIVIDE |
always @(*) |
if ((dcd_DIV)&&(dcd_valid)&&(!dcd_illegal)) |
`ASSERT(dcd_wR); |
|
//////////////////////////////////////////////// |
// |
// Assertions about the op stage |
// |
//////////////////////////////////////////////// |
// |
// |
wire fc_op_illegal, fc_op_wF, fc_op_ALU, fc_op_M, |
fc_op_DV, fc_op_FP, fc_op_break, |
fc_op_lock, fc_op_wR, fc_op_rA, fc_op_rB, |
fc_op_sim; |
wire [6:0] fc_op_Rid, fc_op_Aid, fc_op_Bid; |
wire [31:0] fc_op_I; |
wire [3:0] fc_op_cond; |
wire [3:0] fc_op_op; |
wire [22:0] fc_op_sim_immv; |
wire f_op_insn; //f_alu_insn,f_wb_insn |
reg f_op_phase, f_op_early_branch; |
reg f_op_zI; |
reg f_op_branch; |
|
f_idecode #(.ADDRESS_WIDTH(AW), |
.OPT_MPY((IMPLEMENT_MPY!=0)? 1'b1:1'b0), |
.OPT_EARLY_BRANCHING(EARLY_BRANCHING), |
.OPT_DIVIDE(IMPLEMENT_DIVIDE), |
.OPT_FPU(IMPLEMENT_FPU), |
.OPT_LOCK(OPT_LOCK), |
.OPT_OPIPE(OPT_PIPELINED_BUS_ACCESS), |
.OPT_SIM(1'b0), |
.OPT_CIS(OPT_CIS)) |
f_insn_decode_op(f_op_insn_word, f_op_phase, op_gie, |
fc_op_illegal, fc_op_Rid, fc_op_Aid, fc_op_Bid, |
fc_op_I, fc_op_cond, fc_op_wF, fc_op_op, fc_op_ALU, |
fc_op_M, fc_op_DV, fc_op_FP, fc_op_break, fc_op_lock, |
fc_op_wR, fc_op_rA, fc_op_rB, fc_op_prepipe, |
fc_op_sim, fc_op_sim_immv |
); |
|
initial f_op_early_branch = 1'b0; |
always @(posedge i_clk) |
if (op_ce) |
begin |
f_op_insn_word <= f_dcd_insn_word; |
f_op_phase <= dcd_phase; |
f_op_early_branch <= dcd_early_branch; |
f_op_zI <= dcd_zI; |
end |
|
initial f_op_branch = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(clear_pipeline)) |
f_op_branch <= 1'b0; |
else if (op_ce) |
f_op_branch <= (dcd_early_branch)||dcd_ljmp; |
else if ((adf_ce_unconditional)||(mem_ce)) |
f_op_branch <= 1'b0; |
|
always @(*) |
if (!EARLY_BRANCHING) |
assert(!f_op_branch); |
else if ((f_op_early_branch)&&(op_valid)) |
assert(f_op_branch); |
|
|
always @(posedge i_clk) |
if ((op_valid)&&((f_op_branch)||(!fc_op_illegal))&&(!clear_pipeline)) |
begin |
if (f_op_branch) |
begin |
`ASSERT(!op_valid_alu); |
`ASSERT(!op_valid_mem); |
`ASSERT(!op_valid_div); |
`ASSERT(!op_valid_fpu); |
`ASSERT(!op_illegal); |
`ASSERT(!op_rA); |
`ASSERT(!op_rB); |
`ASSERT(!op_wR); |
`ASSERT(!op_wF); |
`ASSERT(op_opn == `CPU_MOV_OP); |
end |
|
if (op_illegal) |
begin |
`ASSERT(!op_valid_mem); |
`ASSERT(!op_valid_div); |
`ASSERT(!op_valid_fpu); |
`ASSERT( op_valid_alu); |
`ASSERT((!OPT_PIPELINED)||(!op_rA)); |
`ASSERT((!OPT_PIPELINED)||(!op_rB)); |
`ASSERT(!f_op_branch); |
end else begin |
if (!f_op_branch) |
begin |
`ASSERT(fc_op_ALU == op_valid_alu); |
`ASSERT(fc_op_M == op_valid_mem); |
`ASSERT(fc_op_DV == op_valid_div); |
`ASSERT(fc_op_FP == op_valid_fpu); |
`ASSERT(fc_op_rA == op_rA); |
`ASSERT(fc_op_rB == op_rB); |
`ASSERT(fc_op_wF == op_wF); |
`ASSERT(fc_op_Rid[4:0] == op_R); |
`ASSERT(f_op_zI == (fc_op_I == 0)); |
`ASSERT(fc_op_wF == op_wF); |
`ASSERT(fc_op_lock == op_lock); |
`ASSERT(fc_op_break == op_break); |
`ASSERT((!wr_reg_ce)||(wr_reg_id != fc_op_Bid) |
||(!op_rB)||(fc_op_I == 0)); |
`ifdef VERILATOR |
`ASSERT(fc_op_sim == op_sim); |
`ASSERT(fc_op_sim_immv == op_sim_immv); |
`endif |
|
if ((fc_op_wR)&&(fc_op_Rid[4:0] == { op_gie, |
4'he })) |
`ASSERT(!pending_sreg_write); |
|
case(fc_op_cond[2:0]) |
3'h0: `ASSERT(op_F == 8'h00); // Always |
3'h1: `ASSERT(op_F == 8'h11); // Z |
3'h2: `ASSERT(op_F == 8'h44); // LT |
3'h3: `ASSERT(op_F == 8'h22); // C |
3'h4: `ASSERT(op_F == 8'h88); // V |
3'h5: `ASSERT(op_F == 8'h10); // NE |
3'h6: `ASSERT(op_F == 8'h40); // GE (!N) |
3'h7: `ASSERT(op_F == 8'h20); // NC |
endcase |
|
if ((fc_op_wR)&&(fc_op_Rid[4:0] == { gie, `CPU_PC_REG})) |
`ASSERT(!op_phase); |
else |
`ASSERT(f_op_phase == op_phase); |
end // Bit order is { (flags_not_used), VNCZ mask, VNCZ value } |
`ASSERT((!op_wR)||(fc_op_Rid[4:0] == op_R)); |
`ASSERT(((!op_wR)&&(!op_rA))||(fc_op_Aid[4:0] == op_Aid[4:0])); |
`ASSERT((!op_rB)||(fc_op_Bid[4:0] == op_Bid)); |
// |
// if ((!alu_illegal)&&(!ill_err_i)&&(!clear_pipeline)) |
|
if (f_op_early_branch) |
begin |
// `ASSERT(fc_op_Rid[4:0] == { op_gie, `CPU_PC_REG }); |
// `ASSERT(fc_op_wR); |
// `ASSERT(!fc_op_wF); |
`ASSERT(op_opn == `CPU_MOV_OP); |
`ASSERT(!op_wR); |
`ASSERT(!op_wF); |
`ASSERT(f_op_branch); |
end else begin |
`ASSERT(fc_op_op == op_opn); |
`ASSERT(fc_op_wR == op_wR); |
end |
end |
if (!OPT_PIPELINED_BUS_ACCESS) |
`ASSERT((!mem_rdbusy)||(mem_wreg != fc_op_Bid) |
||(!fc_op_rB)||(fc_op_I == 0)); |
end else if ((op_valid)&&(!clear_pipeline)&&(fc_op_illegal)) |
begin |
`ASSERT(op_illegal); |
`ASSERT(op_valid_alu); |
`ASSERT(!f_op_branch); |
end |
|
always @(*) |
if ((op_valid)&&(op_illegal)) |
begin |
`ASSERT(!op_valid_div); |
`ASSERT(!op_valid_fpu); |
`ASSERT(!op_valid_mem); |
end |
|
// always @(*) |
// if (!op_valid) |
// `ASSERT(!op_break); |
|
always @(*) |
if ((!OPT_CIS)&&(op_valid)) |
begin |
`ASSERT((!op_phase)||(op_illegal)); |
`ASSERT(op_pc[1:0] == 2'b0); |
end |
|
always @(*) |
if ((!OPT_LOCK)&&(op_valid)) |
`ASSERT((!op_lock)||(op_illegal)); |
|
always @(*) |
if (!EARLY_BRANCHING) |
assert(!f_op_early_branch); |
|
|
always @(*) |
if (op_ce) |
`ASSERT((dcd_valid)||(dcd_illegal)||(dcd_early_branch)); |
|
|
|
|
|
|
|
always @(*) |
if ((op_valid)&&(op_rB)&&(!f_op_zI)&&(wr_reg_ce)) |
`ASSERT((wr_reg_id != op_Bid)||(dbg_clear_pipe) |
||(wr_reg_id[4:0] == { gie, `CPU_PC_REG})); |
|
always @(*) |
if ((f_past_valid)&&(!f_op_zI)&&(mem_rdbusy)&&(op_valid)&&(op_rB)) |
`ASSERT((!OPT_DCACHE)||(OPT_MEMPIPE) |
||(mem_wreg != op_Bid)); |
|
always @(posedge i_clk) |
if ((op_valid)&&(op_rB)&&(!f_op_zI)&&((mem_rdbusy)||(mem_valid)) |
&&(mem_wreg != {gie, `CPU_PC_REG})) |
begin |
if (!OPT_MEMPIPE) |
begin |
`ASSERT(fc_alu_Aid[4:0] == mem_wreg); |
`ASSERT(mem_wreg != op_Bid); |
end else if (OPT_DCACHE) |
begin |
`ASSERT(fc_alu_Aid[4:0] != op_Bid); |
// It takes two clocks for the DCACHE to announce |
// the value it intends to write to via mem_wreg. |
// At that point, we can make this assertion. So, |
// if the memory is busy reading a value |
if ((!$past(mem_rdbusy)) |
// and we didn't request the read on the last |
// clock, |
&&($past(mem_ce))) |
begin |
// Then the memory should match our last read |
// request. There may be several reads |
// stuffed within the device, so the |
// fc_alu_Bid might not match the mem_wreg, |
// but the rest should be good |
// |
// What we really want to say, isn't valid yet |
// `ASSERT(mem_wreg != op_Bid); |
end else if (($past(mem_rdbusy))&&(!$past(mem_ce)) |
&&(!$past(mem_ce,2))) |
`ASSERT(mem_wreg != op_Bid); |
end else // if (!OPT_DCACHE) |
begin |
if ((mem_valid) |
||($past(mem_rdbusy))) |
`ASSERT(mem_wreg != op_Bid); |
end |
end |
|
// always @(posedge i_clk) |
// if (($fell(mem_rdbusy))&&(mem_valid)) |
// begin |
// `ASSERT(mem_wreg == fc_alu_Aid[4:0]); |
// end |
|
always @(posedge i_clk) |
if (mem_rdbusy) |
begin |
`ASSERT(fc_alu_M); |
`ASSERT((!OPT_PIPELINED)||(fc_alu_wR)); |
end |
|
always @(*) |
if ((op_valid)&&(!clear_pipeline)) |
`ASSERT(op_gie == gie); |
|
always @(*) |
if ((op_valid_alu)&&(!op_illegal)) |
begin |
if ((op_opn != `CPU_SUB_OP) |
&&(op_opn != `CPU_AND_OP) |
&&(op_opn != `CPU_MOV_OP)) |
begin |
`ASSERT(op_wR); |
end |
if ((op_opn != `CPU_MOV_OP)&&(op_opn != `CPU_BREV_OP)) |
`ASSERT(op_rA); |
end |
|
|
always @(posedge i_clk) |
if ((op_valid)&&(!op_illegal) |
&&(!alu_illegal)&&(!ill_err_i)&&(!clear_pipeline)) |
begin |
`ASSERT(op_gie == gie); |
if ((gie)||(op_phase)) |
begin |
`ASSERT((!op_wR)||(op_R[4] == gie)); |
`ASSERT((!op_rA)||(op_Aid[4] == gie)); |
`ASSERT((!op_rB)||(op_Bid[4] == gie)); |
end else if (((op_valid_mem) |
||(op_valid_div)||(op_valid_fpu) |
||((op_valid_alu)&&(op_opn!=`CPU_MOV_OP)))) |
begin |
`ASSERT((!op_wR)||(op_R[4] == gie)); |
`ASSERT((!op_rA)||(op_Aid[4] == gie)); |
`ASSERT((!op_rB)||(op_Bid[4] == gie)); |
end |
end |
|
always @(posedge i_clk) |
if ((!op_valid)&&(!$past(op_illegal)) |
&&(!clear_pipeline)&&(!pending_interrupt)) |
`ASSERT(!op_illegal); |
|
always @(*) |
begin |
if (alu_ce) |
`ASSERT(adf_ce_unconditional); |
if (div_ce) |
`ASSERT(adf_ce_unconditional); |
if (fpu_ce) |
`ASSERT(adf_ce_unconditional); |
|
if ((op_valid)&&(op_illegal)) |
`ASSERT(op_valid_alu); |
end |
|
always @(*) |
if (mem_ce) |
`ASSERT((op_valid)&&(op_valid_mem)&&(!op_illegal)); |
|
always @(*) |
if (div_ce) |
`ASSERT(op_valid_div); |
|
|
always @(*) |
if ((ibus_err_flag)||(ill_err_i)||(idiv_err_flag)) |
begin |
`ASSERT(master_stall); |
`ASSERT(!mem_ce); |
`ASSERT(!alu_ce); |
`ASSERT(!div_ce); |
`ASSERT(!adf_ce_unconditional); |
end |
|
always @(posedge i_clk) |
if ((adf_ce_unconditional)||(mem_ce)) |
`ASSERT(op_valid); |
|
always @(*) |
if ((op_valid_alu)&&(!adf_ce_unconditional)&&(!clear_pipeline)) |
`ASSERT(!op_ce); |
|
always @(*) |
if ((op_valid_div)&&(!adf_ce_unconditional)) |
`ASSERT(!op_ce); |
|
always @(posedge i_clk) |
if (alu_stall) |
`ASSERT(!alu_ce); |
always @(posedge i_clk) |
if (mem_stalled) |
`ASSERT(!mem_ce); |
always @(posedge i_clk) |
if (div_busy) |
`ASSERT(!div_ce); |
|
always @(*) |
if ((!i_reset)&&(break_pending)&&(!clear_pipeline)) |
`ASSERT((op_valid)&&(op_break)); |
|
|
// |
// |
// Op: Memory pipeline assertions |
// |
// |
|
wire [AW-1:0] f_next_mem, f_op_mem_addr; |
assign f_next_mem = mem_addr + 1'b1; |
assign f_op_mem_addr = op_Bv[AW+1:2]; |
|
always @(*) |
if ((op_valid)&&(!fc_alu_prepipe)) |
begin |
`ASSERT((!op_valid_mem)||(!op_pipe)); |
end |
|
always @(*) |
if ((op_valid_mem)&&(op_pipe)) |
begin |
if (mem_rdbusy) |
`ASSERT(op_opn[0] == 1'b0); |
if ((mem_busy)&&(!mem_rdbusy)) |
`ASSERT(op_opn[0] == 1'b1); |
if (mem_rdbusy) |
begin |
if (OPT_PIPELINED_BUS_ACCESS) |
begin end |
else if (OPT_LGDCACHE != 0) |
`ASSERT(mem_wreg != op_Bid); |
end |
end |
|
/* |
always @(*) |
if ((dcd_valid)&&(dcd_pipe)) |
begin |
if ((op_valid_mem)&&(f_op_zI)) |
`ASSERT(dcd_I <= 4); |
if (f_op_zI) |
`ASSERT(dcd_I <= 4); |
// if ((!op_valid)&&(mem_rdbusy)) |
// `ASSERT(mem_wreg != dcd_B); |
end |
*/ |
|
always @(posedge i_clk) |
if ((op_valid_mem)&&(op_pipe)) |
begin |
if ((mem_busy)&&(OPT_LGDCACHE == 0)) |
`ASSERT((f_op_mem_addr == mem_addr) |
||(f_op_mem_addr == f_next_mem)); |
if (mem_valid) |
`ASSERT(op_Bid != mem_wreg); |
|
if (alu_busy||alu_valid) |
`ASSERT((!alu_wR)||(op_Bid != alu_reg)); |
|
if (f_past_valid) |
begin |
if ((mem_busy)&&(OPT_LGDCACHE==0)) |
`ASSERT((op_Bv[(AW+1):2]==mem_addr[(AW-1):0]) |
||(op_Bv[(AW+1):2]==mem_addr[(AW-1):0]+1'b1)); |
|
if ($past(mem_ce)) |
`ASSERT(op_Bid == $past(op_Bid)); |
|
`ASSERT((op_Bid[3:1] != 3'h7)); |
end |
|
if ((mem_rdbusy)||(mem_valid)) |
begin |
if (!OPT_MEMPIPE) |
begin |
`ASSERT(fc_alu_Aid[4:0] == mem_wreg); |
`ASSERT(mem_wreg != op_Bid); |
end else if (OPT_DCACHE) |
begin |
`ASSERT(fc_alu_Aid[4:0] != op_Bid); |
// It takes two clocks for the DCACHE to announce |
// the value it intends to write to via mem_wreg. |
// At that point, we can make this assertion. So, |
// if the memory is busy reading a value |
if ((!$past(mem_rdbusy)) |
// and we didn't request the read on the last |
// clock, |
&&($past(mem_ce))) |
begin |
// Then the memory should match our last read |
// request. There may be several reads |
// stuffed within the device, so the |
// fc_alu_Bid might not match the mem_wreg, |
// but the rest should be good |
// |
// What we really want to say, isn't valid yet |
// `ASSERT(mem_wreg != op_Bid); |
end else if (($past(mem_rdbusy))&&(!$past(mem_ce)) |
&&(!$past(mem_ce,2))) |
`ASSERT(mem_wreg != op_Bid); |
end else // if (!OPT_DCACHE) |
begin |
if ((mem_valid) |
||($past(mem_rdbusy))) |
`ASSERT(mem_wreg != op_Bid); |
end |
end |
end |
|
always @(*) |
if ((dcd_valid)&&(dcd_pipe)) |
`ASSERT((op_Aid[3:1] != 3'h7)||(op_opn[0])); |
|
always @(*) |
if ((op_valid)&(!op_valid_mem)) |
`ASSERT((op_illegal)||(!op_pipe)); |
|
|
//////////////////////////////////////////////// |
// |
// Assertions about the ALU stage |
// |
//////////////////////////////////////////////// |
// |
// |
always @(*) |
if ((alu_ce)&&(!clear_pipeline)) |
`ASSERT((op_valid_alu)&&(op_gie == gie)); |
always @(*) |
if ((mem_ce)&&(!clear_pipeline)) |
`ASSERT((op_valid_mem)&&(op_gie == gie)); |
always @(*) |
if ((div_ce)&&(!clear_pipeline)) |
`ASSERT((op_valid_div)&&(op_gie == gie)); |
|
always @(*) |
if ((!clear_pipeline)&&((mem_valid)||(div_valid)||(div_busy) |
||(mem_rdbusy)||(alu_valid))) |
`ASSERT(alu_gie == gie); |
always @(*) |
if ((!OPT_CIS)&&(alu_pc_valid)) |
`ASSERT(alu_pc[1:0] == 2'b0); |
always @(*) |
if (!OPT_LOCK) |
`ASSERT((!bus_lock)&&(!prelock_stall)); |
always @(*) |
if (!IMPLEMENT_DIVIDE) |
`ASSERT((!dcd_DIV)&&(!op_valid_div)&&(!div_busy)&&(!div_valid)&&(!div_ce)); |
always @(*) |
if (IMPLEMENT_MPY == 0) |
`ASSERT(alu_busy == 1'b0); |
|
|
always @(*) |
if (!clear_pipeline) |
begin |
if ((alu_valid)||(alu_illegal)) |
`ASSERT(alu_gie == gie); |
if (div_valid) |
`ASSERT(alu_gie == gie); |
end |
|
always @(*) |
if (alu_busy) |
begin |
`ASSERT(!mem_rdbusy); |
`ASSERT(!div_busy); |
`ASSERT(!fpu_busy); |
end else if (mem_rdbusy) |
begin |
`ASSERT(!div_busy); |
`ASSERT(!fpu_busy); |
end else if (div_busy) |
`ASSERT(!fpu_busy); |
|
always @(posedge i_clk) |
if ((div_valid)||(div_busy)) |
`ASSERT(alu_reg[3:1] != 3'h7); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(wr_reg_ce) |
&&((!$past(r_halted))||(!$past(i_dbg_we)))) |
`ASSERT(alu_gie == gie); |
|
|
|
|
wire [31:0] f_Bv; |
reg [31:0] f_Av, f_pre_Bv; |
|
// |
// |
// The A operand |
// |
// |
always @(*) |
begin |
f_Av = regset[fc_op_Aid[4:0]]; |
if (fc_op_Aid[3:0] == `CPU_PC_REG) |
begin |
if ((wr_reg_ce)&&(wr_reg_id == fc_op_Aid[4:0])) |
f_Av = wr_spreg_vl; |
else if (fc_op_Aid[4] == op_gie) |
f_Av = op_pc; // f_next_addr; |
else if (fc_op_Aid[3:0] == { 1'b1, `CPU_PC_REG }) |
begin |
f_Av[31:(AW+1)] = 0; |
f_Av[(AW+1):0] = { upc, uhalt_phase, 1'b0 }; |
end |
end else if (fc_op_Aid[4:0] == { 1'b0, `CPU_CC_REG }) |
begin |
f_Av = { w_cpu_info, regset[fc_op_Aid[4:0]][22:16], 1'b0, w_iflags }; |
if ((wr_reg_ce)&&(wr_reg_id == fc_op_Aid[4:0])) |
f_Av[22:16] = wr_spreg_vl[22:16]; |
end else if (fc_op_Aid[4:0] == { 1'b1, `CPU_CC_REG }) |
begin |
f_Av = { w_cpu_info, regset[fc_op_Aid[4:0]][22:16], 1'b1, w_uflags }; |
if ((wr_reg_ce)&&(wr_reg_id == fc_op_Aid[4:0])) |
f_Av[22:16] = wr_spreg_vl[22:16]; |
end else if ((wr_reg_ce)&&(wr_reg_id == fc_op_Aid[4:0])) |
f_Av = wr_gpreg_vl; |
else |
f_Av = regset[fc_op_Aid[4:0]]; |
end |
|
// |
// |
// The B operand |
// |
// |
|
// The PRE-logic |
always @(*) |
begin |
f_pre_Bv = regset[fc_op_Bid[4:0]]; |
// |
if (fc_op_Bid[3:0] == `CPU_PC_REG) |
begin |
// Can always read your own address |
if (fc_op_Bid[4] == op_gie) |
f_pre_Bv = { {(30-AW){1'b0}}, op_pc[(AW+1):2], 2'b00 }; // f_next_addr; |
else // if (fc_op_Bid[4]) |
// Supervisor or user may read the users PC reg |
begin |
f_pre_Bv = 0; |
f_pre_Bv[(AW+1):0] = { upc[(AW+1):2], uhalt_phase, 1'b0 }; |
if ((wr_reg_ce)&&(wr_reg_id == fc_op_Bid[4:0])) |
f_pre_Bv = wr_spreg_vl; |
end |
end else if (fc_op_Bid[3:0] == `CPU_CC_REG) |
begin |
f_pre_Bv = { w_cpu_info, regset[fc_op_Bid[4:0]][22:16], 1'b0, |
w_uflags }; |
if ((fc_op_Bid[4] == op_gie)&&(!fc_op_Bid[4])) |
f_pre_Bv[14:0] = (op_gie) ? w_uflags : w_iflags; |
|
if ((wr_reg_ce)&&(wr_reg_id == fc_op_Bid[4:0])) |
f_pre_Bv[22:16] = wr_spreg_vl[22:16]; |
|
end else if ((wr_reg_ce)&&(wr_reg_id == fc_op_Bid[4:0])) |
f_pre_Bv = wr_gpreg_vl; |
else |
f_pre_Bv = regset[fc_op_Bid[4:0]]; |
end |
|
|
// The actual calculation of B |
assign f_Bv = (fc_op_rB) |
? ((fc_op_Bid[5]) |
? ( { f_pre_Bv }+{ fc_op_I[29:0],2'b00 }) |
: (f_pre_Bv + fc_op_I)) |
: fc_op_I; |
|
|
//////////////////////////////// |
// |
// CONTRACT: The operands to an ALU/MEM/DIV operation |
// must be valid. |
// |
always @(posedge i_clk) |
if ((op_valid)&&(!op_illegal)&&(!clear_pipeline)) |
begin |
if (((!wr_reg_ce)||(wr_reg_id!= { gie, `CPU_PC_REG })) |
&&(!dbg_clear_pipe)&&(!clear_pipeline)&&(!f_op_branch)) |
begin |
if ((fc_op_rA)&&(fc_op_Aid[3:1] != 3'h7)) |
`ASSERT(f_Av == op_Av); |
`ASSERT(f_Bv == op_Bv); |
end |
end |
|
//////////////////////////////////////////////////////////////// |
// |
// |
// Pipeline signaling check |
// |
// |
//////////////////////////////////////////////////////////////// |
// |
// |
|
// |
// Assertions about the prefetch |
// Assertions about the decode stage |
// dcd_ce, dcd_valid |
assign f_dcd_data = { |
dcd_phase, |
dcd_opn, dcd_A, dcd_B, dcd_R, // 4+15 |
dcd_Acc, dcd_Bcc, dcd_Apc, dcd_Bpc, dcd_Rcc, dcd_Rpc,//6 |
dcd_F, // 4 |
dcd_wR, dcd_rA, dcd_rB, |
dcd_ALU, dcd_M, dcd_DIV, dcd_FP, |
dcd_wF, dcd_gie, dcd_break, dcd_lock, |
dcd_pipe, dcd_ljmp, |
dcd_pc, // AW+1 |
dcd_I, // 32 |
dcd_zI, // true if dcd_I == 0 |
dcd_illegal, |
dcd_early_branch, |
dcd_sim, dcd_sim_immv |
}; |
|
//////////////////////////////////////////////// |
// |
// Assertions about the prefetch (output) stage |
// |
//////////////////////////////////////////////// |
|
//////////////////////////////////////////////// |
// |
// Assertions about the op stage |
// |
//////////////////////////////////////////////// |
// op_valid |
// op_ce |
// op_stall |
wire [4+AW+2+7+4-1:0] f_op_data; |
assign f_op_data = { op_valid_mem, op_valid_alu, |
op_valid_div, op_valid_fpu, |
// The Av and Bv values can change while we are stalled in the |
// op stage--that's why we are stalled there |
// r_op_Av, r_op_Bv, // 32 ea |
op_pc[AW+1:2], // AW |
op_wR, op_wF, |
r_op_F, // 7 |
op_illegal, op_break, |
op_lock, op_pipe |
}; |
|
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(op_valid))&&(!$past(i_reset)) |
&&(!$past(clear_pipeline))) |
begin |
if (($past(op_valid_mem))&&($past(mem_stalled))) |
`ASSERT($stable(f_op_data[AW+16:1])&&(!$rose(op_pipe))); |
if (($past(op_valid_div))&&($past(div_busy))) |
`ASSERT($stable(f_op_data)); |
end |
|
///////// |
// |
// CIS instructions, enabled by OPT_CIS |
// |
///////// |
|
//////////////////////////////////////////////// |
// |
// Assertions about the ALU stage |
// |
//////////////////////////////////////////////// |
// |
// |
// alu_valid |
// alu_ce |
// alu_stall |
// ALU stage assertions |
|
reg f_alu_branch; |
|
always @(posedge i_clk) |
if ((alu_ce)||(mem_ce)||(div_ce)||(fpu_ce)) |
begin |
f_alu_insn_word <= f_op_insn_word; |
f_alu_phase <= f_op_phase; |
end |
|
initial f_alu_branch = 1'b0; |
always @(posedge i_clk) |
if ((adf_ce_unconditional)||(mem_ce)) |
f_alu_branch <= f_op_branch; |
else |
f_alu_branch <= 1'b0; |
|
|
wire fc_alu_illegal, fc_alu_wF, fc_alu_ALU, fc_alu_DV, |
fc_alu_FP, fc_alu_break, fc_alu_lock, |
fc_alu_rA, fc_alu_rB, fc_alu_sim; |
wire [6:0] fc_alu_Rid, fc_alu_Bid; |
wire [31:0] fc_alu_I; |
wire [3:0] fc_alu_cond; |
wire [3:0] fc_alu_op; |
wire [22:0] fc_alu_sim_immv; |
|
f_idecode #(.ADDRESS_WIDTH(AW), |
.OPT_MPY((IMPLEMENT_MPY!=0)? 1'b1:1'b0), |
.OPT_EARLY_BRANCHING(EARLY_BRANCHING), |
.OPT_DIVIDE(IMPLEMENT_DIVIDE), |
.OPT_FPU(IMPLEMENT_FPU), |
.OPT_LOCK(OPT_LOCK), |
.OPT_OPIPE(OPT_PIPELINED_BUS_ACCESS), |
.OPT_SIM(1'b0), |
.OPT_CIS(OPT_CIS)) |
f_insn_decode_alu(f_alu_insn_word, f_alu_phase, alu_gie, |
fc_alu_illegal, fc_alu_Rid, fc_alu_Aid, fc_alu_Bid, |
fc_alu_I, fc_alu_cond, fc_alu_wF, fc_alu_op, fc_alu_ALU, |
fc_alu_M, fc_alu_DV, fc_alu_FP, fc_alu_break, |
fc_alu_lock, fc_alu_wR, fc_alu_rA, fc_alu_rB, |
fc_alu_prepipe, fc_alu_sim, fc_alu_sim_immv |
); |
|
always @(posedge i_clk) |
if (!wr_reg_ce) |
begin |
if (f_alu_branch) |
begin |
`ASSERT((!div_valid)&&(!div_busy)); |
`ASSERT((!fpu_valid)&&(!fpu_busy)); |
`ASSERT(!mem_rdbusy); |
`ASSERT(!alu_busy); |
end else begin |
if (!fc_alu_DV) |
`ASSERT((!div_valid)&&(!div_busy)&&(!div_error)); |
if (!fc_alu_M) |
`ASSERT(!mem_rdbusy); |
if (!fc_alu_ALU) |
`ASSERT(!alu_busy); |
if (!fc_alu_FP) |
`ASSERT((!fpu_busy)&&(!fpu_error)); |
if (alu_busy) |
`ASSERT((fc_alu_op[3:1] == 3'h5) |
||(fc_alu_op[3:0] == 4'hc)); |
if ((alu_busy)||(div_busy)||(fpu_busy)) |
begin |
`ASSERT(!mem_rdbusy); |
`ASSERT((clear_pipeline) |
||(fc_alu_Rid[4:0] == alu_reg)); |
if (alu_busy) |
`ASSERT(fc_alu_wF == alu_wF); |
if ((fc_alu_Rid[3:1] == 3'h7)&&(alu_wR) |
&&(fc_alu_Rid[4:0] != { gie, 4'hf })) |
`ASSERT(pending_sreg_write); |
end else if (mem_rdbusy) |
begin |
if ($past(mem_rdbusy)) |
`ASSERT(fc_alu_Rid[4] == mem_wreg[4]); |
// |
end |
|
//if ((div_busy)||(fpu_busy)) |
// `ASSERT(alu_wR); |
//else |
if ((alu_busy)&&(alu_wR)) |
`ASSERT(fc_alu_wR); |
|
if (alu_busy || mem_rdbusy || div_busy) |
begin |
if ((fc_alu_wR)&&(fc_alu_Rid[4:0] == { gie, `CPU_PC_REG})) |
`ASSERT(!alu_phase); |
else |
`ASSERT(f_alu_phase == alu_phase); |
end |
end |
|
end else if (!dbgv) // && wr_reg_ce |
begin |
`ASSERT(fc_alu_DV || (!div_valid)&&(!div_error)); |
`ASSERT(fc_alu_ALU|| !alu_valid); |
`ASSERT(fc_alu_M || !mem_valid); |
`ASSERT(fc_alu_FP || (!fpu_valid)&&(!fpu_error)); |
`ASSERT((!alu_busy)&&(!div_busy)&&(!fpu_busy)); |
|
if ((!OPT_PIPELINED_BUS_ACCESS)||((!mem_valid)&&(!mem_rdbusy))) |
`ASSERT(fc_alu_Rid[4:0] == wr_reg_id); |
if ((!alu_illegal)&&(fc_alu_cond[3])&&(fc_alu_wR)&&(fc_alu_ALU)) |
`ASSERT(alu_wR); |
if (!mem_valid) |
`ASSERT(fc_alu_Rid[4:0] == alu_reg); |
`ASSERT((!alu_wR)||(fc_alu_wR == alu_wR)); |
if (alu_valid) |
`ASSERT(fc_alu_wF == alu_wF); |
if (!fc_alu_wF) |
`ASSERT(!wr_flags_ce); |
|
// `ASSERT(pending_sreg_write |
// == ((OPT_PIPELINED)&&(wr_reg_id[3:1]==3'h7))); |
|
`ASSERT(!f_alu_branch); |
end |
|
always @(posedge i_clk) |
if (f_mem_pc) |
begin |
if ((!OPT_DCACHE)||(!OPT_MEMPIPE)) |
`ASSERT(!fc_alu_prepipe); |
else if ((mem_rdbusy)&&(!$past(mem_ce))&&(!$past(mem_ce,2))) |
`ASSERT(!fc_alu_prepipe); |
end |
|
always @(posedge i_clk) |
if (mem_rdbusy) |
begin |
// In pipelined mode, this is an ongoing load operation |
// Otherwise, mem_rdbusy == mem_busy and we have no idea |
// what type of operation we are in |
`ASSERT(!fc_alu_illegal); |
`ASSERT(fc_alu_M); |
if (OPT_PIPELINED) |
begin |
`ASSERT(fc_alu_wR); |
end if (!OPT_PIPELINED_BUS_ACCESS) |
`ASSERT(fc_alu_Rid[4:0] == mem_wreg); |
|
if ((fc_alu_wR)&&(fc_alu_Rid[4:0] == { gie, `CPU_PC_REG})) |
`ASSERT(!alu_phase); |
else |
`ASSERT(f_alu_phase == alu_phase); |
end else if ((mem_busy)&&(fc_alu_M)) |
begin |
// Ongoing store operation |
`ASSERT(!fc_alu_illegal); |
`ASSERT(fc_alu_M); |
`ASSERT(!fc_alu_wR); |
end |
|
// always @(posedge i_clk) |
// if ((OPT_PIPELINED)&&(cc_invalid_for_dvd)) |
// begin |
// assert((op_valid &&(fc_op_wF |
// ||(fc_op_wR &&(fc_op_Aid[3:0]==`CPU_CC_REG)))) |
// ||fc_alu_wF |
// ||((fc_alu_wR &&(fc_alu_Aid[3:0] == `CPU_CC_REG)))); |
// end |
|
|
//////////////////////////////////////////////// |
// |
// Assertions about the writeback stage |
// |
//////////////////////////////////////////////// |
// |
// |
initial assert((!OPT_LOCK)||(OPT_PIPELINED)); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_reset))&&($past(gie) != gie)) |
`ASSERT(clear_pipeline); |
|
always @(*) |
if (!IMPLEMENT_FPU) |
begin |
`ASSERT(!ifpu_err_flag); |
`ASSERT(!ufpu_err_flag); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(r_halted)) |
begin |
`ASSERT(!div_busy); |
`ASSERT(!mem_busy); |
`ASSERT(!alu_busy); |
`ASSERT(!div_valid); |
`ASSERT(!mem_valid); |
`ASSERT(!alu_valid); |
end |
|
always @(*) |
if (((wr_reg_ce)||(wr_flags_ce))&&(!dbgv)) |
`ASSERT(!alu_illegal); |
|
always @(*) |
if (wr_reg_ce) |
begin |
`ASSERT(fc_alu_wR); |
|
// Since writes are asynchronous, they can create errors later |
`ASSERT((!bus_err)||(!mem_valid)); |
`ASSERT(!fpu_error); |
`ASSERT(!div_error); |
end |
|
|
////////////////////////////////////////////// |
// |
// |
// Tying together the WB requests and acks |
// |
// |
////////////////////////////////////////////// |
// |
// |
always @(*) |
begin |
if (mem_cyc_gbl) |
begin |
`ASSERT(f_gbl_mem_nreqs == f_mem_nreqs); |
`ASSERT(f_gbl_mem_nacks == f_mem_nacks); |
end |
if (mem_cyc_lcl) |
begin |
`ASSERT(f_lcl_mem_nreqs == f_mem_nreqs); |
`ASSERT(f_lcl_mem_nacks == f_mem_nacks); |
end |
|
`ASSERT(f_gbl_pf_nreqs == f_pf_nreqs); |
`ASSERT(f_gbl_pf_nacks == f_pf_nreqs); |
`ASSERT(f_gbl_pf_outstanding == f_pf_outstanding); |
|
`ASSERT(f_lcl_pf_nreqs == 0); |
`ASSERT(f_lcl_pf_nacks == 0); |
end |
|
////////////////////////////////////////////// |
// |
// |
// Ad-hoc (unsorted) properties |
// |
// |
////////////////////////////////////////////// |
// |
// |
// |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(mem_rdbusy)) |
&&(!$past(mem_valid)||($past(mem_wreg[3:1] != 3'h7)))) |
`ASSERT(mem_wreg[4] == alu_gie); |
always @(posedge i_clk) |
if (mem_valid) |
`ASSERT(mem_wreg[4] == alu_gie); |
|
|
|
// Break instructions are not allowed to move past the op stage |
always @(*) |
if ((break_pending)||(op_break)) |
`ASSERT((!alu_ce)&&(!mem_ce)&&(!div_ce)&&(!fpu_ce)); |
|
always @(*) |
if (op_break) |
`ASSERT((!alu_ce)&&(!mem_ce)&&(!div_ce)&&(!fpu_ce)); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset)) |
&&($past(break_pending))&&(!break_pending)) |
`ASSERT((clear_pipeline)||($past(clear_pipeline))); |
|
always @(*) |
if ((o_break)||((alu_valid)&&(alu_illegal))) |
begin |
`ASSERT(!alu_ce); |
`ASSERT(!mem_ce); |
`ASSERT(!div_ce); |
`ASSERT(!fpu_ce); |
`ASSERT(!mem_rdbusy); |
// The following two shouldn't be true, but will be true |
// following a bus error |
if (!bus_err) |
begin |
`ASSERT(!alu_busy); |
`ASSERT(!div_busy); |
`ASSERT(!fpu_busy); |
end |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&(!$past(clear_pipeline))&& |
($past(div_busy))&&(!clear_pipeline)) |
begin |
`ASSERT($stable(alu_reg)); |
`ASSERT(alu_reg[4] == alu_gie); |
`ASSERT($stable(alu_pc)); |
`ASSERT($stable(alu_phase)); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&(!i_reset) |
&&(!$past(clear_pipeline))&&(!clear_pipeline) |
&&(($past(div_busy))||($past(mem_rdbusy)))) |
`ASSERT($stable(alu_gie)); |
|
always @(posedge i_clk) |
if (mem_rdbusy) |
`ASSERT(!new_pc); |
|
always @(posedge i_clk) |
if ((wr_reg_ce)&&((wr_write_cc)||(wr_write_pc))) |
`ASSERT(wr_spreg_vl == wr_gpreg_vl); |
|
// always @(posedge i_clk) |
// if ((f_past_valid)&&(alu_gie)&&(wr_reg_ce) |
// &&((!$past(r_halted))||(!$past(i_dbg_we)))) |
// `ASSERT(wr_reg_id[4]); |
// else if ((alu_gie)&&((alu_busy)||(div_busy))) |
// `ASSERT((!alu_wR)||(alu_reg[4])); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(clear_pipeline))&&(!$past(i_reset)) |
&&($past(op_valid))&&($past(op_illegal))&&(!op_illegal)) |
`ASSERT(alu_illegal); |
|
always @(*) |
if ((OPT_PIPELINED)&&(alu_valid)&&(alu_wR)&&(!clear_pipeline) |
&&(alu_reg[3:1] == 3'h7) |
&&(alu_reg[4:0] != { gie, `CPU_PC_REG })) |
`ASSERT(pending_sreg_write); |
|
always @(posedge i_clk) |
if ((OPT_PIPELINED)&&(mem_valid)&&(mem_wreg[3:1] == 3'h7) |
&&(mem_wreg[4:0] != { gie, `CPU_PC_REG })) |
`ASSERT(pending_sreg_write); // ! |
else if ((OPT_PIPELINED)&&(OPT_DCACHE)&&(mem_rdbusy) |
&&($past(mem_rdbusy)) |
&&($past(mem_rdbusy,2))) |
`ASSERT((mem_wreg[3:1] != 3'h7) |
||(mem_wreg == { gie, `CPU_PC_REG}) |
||(pending_sreg_write)); |
// &&(mem_wreg[4:0] != { gie, `CPU_PC_REG })&&(mem_rdbusy)) |
// `ASSERT(pending_sreg_write); |
|
always @(*) |
if ((op_valid_alu)||(op_valid_div)||(op_valid_mem)||(op_valid_fpu)) |
`ASSERT(op_valid); |
|
always @(*) |
if (!OPT_PIPELINED) |
begin |
if (op_valid) |
begin |
`ASSERT(!dcd_valid); |
`ASSERT(!mem_busy); |
`ASSERT(!alu_busy); |
`ASSERT(!div_busy); |
`ASSERT((!wr_reg_ce)||(dbgv)); |
`ASSERT(!wr_flags_ce); |
end |
end |
|
always @(posedge i_clk) |
if ((!OPT_PIPELINED)&&(f_past_valid)) |
begin |
if (op_valid) |
`ASSERT($stable(f_dcd_insn_word)); |
end |
|
|
|
always @(posedge i_clk) |
if ((alu_ce)||(div_ce)||(fpu_ce)) |
`ASSERT(adf_ce_unconditional); |
|
always @(posedge i_clk) |
if ((!clear_pipeline)&&(master_ce)&&(op_ce)&&(op_valid)) |
begin |
if (op_valid_mem) |
`ASSERT((mem_ce)||(!set_cond)); |
else begin |
`ASSERT(!master_stall); |
if ((set_cond)&&(op_valid_div)) |
`ASSERT(div_ce||pending_sreg_write); |
if (!op_valid_alu) |
assert(!alu_ce); |
end |
end |
|
////////////////////////////////////////////// |
// |
// |
// Cover statements |
// |
// |
////////////////////////////////////////////// |
always @(posedge i_clk) |
if ((gie)&&(wr_reg_ce)) |
begin |
// Cover the switch to interrupt |
cover((i_interrupt)&&(!alu_phase)&&(!bus_lock)); |
|
// Cover a "step" instruction |
cover(((alu_pc_valid)||(mem_pc_valid)) |
&&(step)&&(!alu_phase)&&(!bus_lock)); |
|
// Cover a break instruction |
cover((master_ce)&&(break_pending)&&(!break_en)); |
|
// Cover an illegal instruction |
cover((alu_illegal)&&(!clear_pipeline)); |
|
// Cover a division by zero |
cover(div_error); |
|
// Cover a bus error |
cover(bus_err); |
|
// Cover a TRAP instruction to the CC register |
cover(((wr_reg_ce)&&(!wr_spreg_vl[`CPU_GIE_BIT]) |
&&(wr_reg_id[4])&&(wr_write_cc))); |
end |
|
////////////////////////////////////////////// |
////////////////////////////////////////////// |
// |
// always @(*) |
// if (break_pending) |
// `ASSERT((op_valid)&&(op_break)); |
////////////////////////////////////////////// |
// |
// |
// Problem limiting assumptions |
// |
// |
////////////////////////////////////////////// |
// |
// Careless assumptions might be located here |
|
always @(*) |
assume(fc_op_Aid[3:0] != `CPU_CC_REG); |
always @(*) |
assume(fc_op_Bid[3:0] != `CPU_CC_REG); |
|
always @(*) |
assume(!i_halt); |
|
`endif // FORMAL |
//}}} |
|
endmodule |
/core/dcache.v
40,15 → 40,15
// virtual page size--lest in the middle of reading a page a TLB miss |
// take place referencing only a part of the cacheable page. |
// |
// |
// |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2016-2019, Gisselquist Technology, LLC |
// Copyright (C) 2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
67,146 → 67,88
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
// |
// |
`ifdef FORMAL |
`define ASSERT assert |
|
`ifdef DCACHE |
`define ASSUME assume |
`else |
`define ASSUME assert |
`endif |
`endif |
|
module dcache(i_clk, i_reset, i_pipe_stb, i_lock, |
module dcache(i_clk, i_rst, i_pipe_stb, i_lock, |
i_op, i_addr, i_data, i_oreg, |
o_busy, o_pipe_stalled, o_valid, o_err, o_wreg,o_data, |
o_wb_cyc_gbl, o_wb_cyc_lcl, o_wb_stb_gbl, o_wb_stb_lcl, |
o_wb_we, o_wb_addr, o_wb_data, o_wb_sel, |
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data |
`ifdef FORMAL |
, f_nreqs, f_nacks, f_outstanding, f_pc |
`endif |
); |
o_wb_we, o_wb_addr, o_wb_data, |
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data); |
parameter LGCACHELEN = 8, |
ADDRESS_WIDTH=30, |
LGNLINES=(LGCACHELEN-3), // Log of the number of separate cache lines |
ADDRESS_WIDTH=32, |
LGNLINES=5, // Log of the number of separate cache lines |
IMPLEMENT_LOCK=0, |
NAUX=5; // # of aux d-wires to keep aligned w/memops |
parameter [0:0] OPT_LOCAL_BUS=1'b1; |
parameter [0:0] OPT_PIPE=1'b1; |
parameter [0:0] OPT_LOCK=1'b1; |
parameter [0:0] OPT_DUAL_READ_PORT=1'b1; |
parameter OPT_FIFO_DEPTH = 4; |
localparam SDRAM_BIT = 26; |
localparam FLASH_BIT = 22; |
localparam BLKRAM_BIT= 15; |
localparam AW = ADDRESS_WIDTH; // Just for ease of notation below |
localparam CS = LGCACHELEN; // Number of bits in a cache address |
localparam LS = CS-LGNLINES; // Bits to spec position w/in cline |
parameter F_LGDEPTH=1 + (((!OPT_PIPE)||(LS > OPT_FIFO_DEPTH)) |
? LS : OPT_FIFO_DEPTH); |
localparam LGAUX = 3; // log_2 of the maximum number of piped data |
localparam DW = 32; // Bus data width |
localparam DP = OPT_FIFO_DEPTH; |
// |
localparam [1:0] DC_IDLE = 2'b00; // Bus is idle |
localparam [1:0] DC_WRITE = 2'b01; // Write |
localparam [1:0] DC_READS = 2'b10; // Read a single value(!cachd) |
localparam [1:0] DC_READC = 2'b11; // Read a whole cache line |
// |
input wire i_clk, i_reset; |
localparam LGAUX = 3; // log_2 of the maximum number of piped data |
input i_clk, i_rst; |
// Interface from the CPU |
input wire i_pipe_stb, i_lock; |
input wire [2:0] i_op; |
input wire [(DW-1):0] i_addr; |
input wire [(DW-1):0] i_data; |
input wire [(NAUX-1):0] i_oreg; // Aux data, such as reg to write to |
input i_pipe_stb, i_lock; |
input i_op; |
input [31:0] i_addr; |
input [31:0] i_data; |
input [(NAUX-1):0] i_oreg; // Aux data, such as reg to write to |
// Outputs, going back to the CPU |
output reg o_busy; |
output reg o_pipe_stalled; |
output reg o_valid, o_err; |
output wire o_busy, o_pipe_stalled, o_valid, o_err; |
output reg [(NAUX-1):0] o_wreg; |
output reg [(DW-1):0] o_data; |
output reg [31:0] o_data; |
// Wishbone bus master outputs |
output wire o_wb_cyc_gbl, o_wb_cyc_lcl; |
output reg o_wb_stb_gbl, o_wb_stb_lcl; |
output reg o_wb_we; |
output reg [(AW-1):0] o_wb_addr; |
output reg [(DW-1):0] o_wb_data; |
output wire [(DW/8-1):0] o_wb_sel; |
output reg [(AW-1):0] o_wb_addr; |
output reg [31:0] o_wb_data; |
// Wishbone bus slave response inputs |
input wire i_wb_ack, i_wb_stall, i_wb_err; |
input wire [(DW-1):0] i_wb_data; |
`ifdef FORMAL |
output wire [(F_LGDEPTH-1):0] f_nreqs, f_nacks, f_outstanding; |
output wire f_pc; |
input i_wb_ack, i_wb_stall, i_wb_err; |
input [31:0] i_wb_data; |
|
reg f_past_valid; |
`endif |
// |
// output reg [31:0] o_debug; |
|
|
reg cyc, stb, last_ack, end_of_line, last_line_stb; |
reg r_wb_cyc_gbl, r_wb_cyc_lcl; |
// npending is the number of pending non-cached operations, counted |
// from the i_pipe_stb to the o_wb_ack |
reg [DP:0] npending; |
|
|
reg [((1<<LGNLINES)-1):0] c_v; // One bit per cache line, is it valid? |
reg [(AW-LS-1):0] c_vtags [0:((1<<LGNLINES)-1)]; |
reg [(DW-1):0] c_mem [0:((1<<CS)-1)]; |
reg set_vflag; |
reg [1:0] state; |
reg [(CS-1):0] wr_addr; |
reg [(DW-1):0] cached_idata, cached_rdata; |
reg [DW-1:0] pre_data; |
reg lock_gbl, lock_lcl; |
reg [31:0] c_mem [0:((1<<CS)-1)]; |
// reg [((1<<LGNLINES)-1):0] c_wr; // Is the cache line writable? |
// reg c_wdata; |
// reg c_waddr; |
|
|
// To simplify writing to the cache, and the job of the synthesizer to |
// recognize that a cache write needs to take place, we'll take an extra |
// clock to get there, and use these c_w... registers to capture the |
// data in the meantime. |
reg c_wr; |
reg [(DW-1):0] c_wdata; |
reg [(DW/8-1):0] c_wsel; |
reg [31:0] c_wdata; |
reg [(CS-1):0] c_waddr; |
|
reg [(AW-LS-1):0] last_tag; |
reg last_tag_valid; |
|
|
wire [(LGNLINES-1):0] i_cline; |
wire [(CS-1):0] i_caddr; |
wire [(AW-LS-1):0] i_ctag; |
|
`ifdef FORMAL |
reg [F_LGDEPTH-1:0] f_fill; |
reg [AW:0] f_return_address; |
reg [AW:0] f_pending_addr; |
`endif |
assign i_cline = i_addr[(CS-1):LS]; |
assign i_caddr = i_addr[(CS-1):0]; |
assign i_ctag = i_addr[(AW-1):LS]; |
|
assign i_cline = i_addr[(CS+1):LS+2]; |
assign i_caddr = i_addr[(CS+1):2]; |
|
wire cache_miss_inow, w_cachable; |
assign cache_miss_inow = (!last_tag_valid) |
||(last_tag != i_addr[(AW+1):LS+2]) |
||(!c_v[i_cline]); |
assign cache_miss_inow = (last_tag != i_addr[31:LS])||(!c_v[i_cline]); |
assign w_cachable = (i_addr[31:30]!=2'b11)&&(!i_lock)&&( |
((SDRAM_BIT>0)&&(i_addr[SDRAM_BIT])) |
||((FLASH_BIT>0)&&(i_addr[FLASH_BIT])) |
||((BLKRAM_BIT>0)&&(i_addr[BLKRAM_BIT]))); |
|
wire raw_cachable_address; |
|
iscachable chkaddress(i_addr[AW+1:2], raw_cachable_address); |
|
assign w_cachable = ((!OPT_LOCAL_BUS)||(i_addr[(DW-1):(DW-8)]!=8'hff)) |
&&((!i_lock)||(!OPT_LOCK))&&(raw_cachable_address); |
|
reg r_cachable, r_svalid, r_dvalid, r_rd, r_cache_miss, |
r_rd_pending; |
reg [(AW-1):0] r_addr; |
reg r_cachable, r_svalid, r_dvalid, r_rd, r_cache_miss, r_rvalid; |
reg [(AW-1):0] r_addr; |
reg [31:0] r_idata, r_ddata, r_rdata; |
wire [(LGNLINES-1):0] r_cline; |
wire [(CS-1):0] r_caddr; |
wire [(AW-LS-1):0] r_ctag; |
wire [(CS-1):0] r_caddr; |
wire [(AW-LS-1):0] r_ctag; |
|
assign r_cline = r_addr[(CS-1):LS]; |
assign r_caddr = r_addr[(CS-1):0]; |
213,14 → 155,9
assign r_ctag = r_addr[(AW-1):LS]; |
|
|
reg wr_cstb, r_iv, in_cache; |
reg wr_cstb, r_iv, pipeable_op, non_pipeable_op, in_cache; |
reg [(AW-LS-1):0] r_itag; |
reg [DW/8-1:0] r_sel; |
reg [(NAUX+4-1):0] req_data; |
reg gie; |
|
|
|
// |
// The one-clock delayed read values from the cache. |
// |
228,28 → 165,15
initial r_cachable = 1'b0; |
initial r_svalid = 1'b0; |
initial r_dvalid = 1'b0; |
initial r_cache_miss = 1'b0; |
initial r_addr = 0; |
initial last_tag_valid = 0; |
initial r_rd_pending = 0; |
always @(posedge i_clk) |
if (i_reset) |
begin |
r_rd <= 1'b0; |
r_cachable <= 1'b0; |
r_svalid <= 1'b0; |
r_dvalid <= 1'b0; |
r_cache_miss <= 1'b0; |
r_addr <= 0; |
r_rd_pending <= 0; |
last_tag_valid <= 0; |
end else begin |
// The single clock path |
r_idata <= c_mem[i_addr[(CS-1):0]]; |
// The valid for the single clock path |
// Only ... we need to wait if we are currently writing |
// to our cache. |
r_svalid<= (i_pipe_stb)&&(!i_op[0])&&(w_cachable) |
&&(!cache_miss_inow)&&(!c_wr)&&(!wr_cstb); |
r_svalid<= (!i_op)&&(!cache_miss_inow)&&(w_cachable) |
&&(i_pipe_stb)&&(!c_wr)&&(!wr_cstb); |
|
// |
// The two clock in-cache path |
256,37 → 180,25
// |
// Some preliminaries that needed to be calculated on the first |
// clock |
if ((!o_pipe_stalled)&&(!r_rd_pending)) |
r_addr <= i_addr[(AW+1):2]; |
if ((!o_pipe_stalled)&&(!r_rd_pending)) |
if (!o_busy) |
begin |
r_iv <= c_v[i_cline]; |
r_itag <= c_vtags[i_cline]; |
r_cachable <= (!i_op[0])&&(w_cachable)&&(i_pipe_stb); |
r_rd_pending <= (i_pipe_stb)&&(!i_op[0])&&(w_cachable) |
&&((cache_miss_inow)||(c_wr)||(wr_cstb)); |
// &&((!c_wr)||(!wr_cstb)); |
r_addr <= i_addr; |
r_cachable <= (!i_op)&&(w_cachable)&&(i_pipe_stb); |
end else begin |
r_iv <= c_v[r_cline]; |
r_itag <= c_vtags[r_cline]; |
r_rd_pending <= (r_rd_pending) |
&&((!cyc)||(!i_wb_err)) |
&&((r_itag != r_ctag)||(!r_iv)); |
end |
r_rd <= (i_pipe_stb)&&(!i_op[0]); |
// r_idata still contains the right answer |
r_rd <= (i_pipe_stb)&&(!i_op); |
r_ddata <= r_idata; |
// r_itag contains the tag we didn't have available to us on the |
// last clock, r_ctag is a bit select from r_addr containing a |
// one clock delayed address. |
r_dvalid <= (!r_svalid)&&(!r_dvalid)&&(r_itag == r_ctag)&&(r_iv) |
&&(r_cachable)&&(r_rd_pending); |
if ((r_itag == r_ctag)&&(r_iv)&&(r_cachable)&&(r_rd_pending)) |
begin |
last_tag_valid <= 1'b1; |
r_dvalid <= (r_itag == r_ctag)&&(r_iv)&&(r_cachable); |
if ((r_itag == r_ctag)&&(r_iv)&&(r_cachable)) |
last_tag <= r_ctag; |
end else if ((state == DC_READC) |
&&(last_tag[CS-LS-1:0]==o_wb_addr[CS-1:LS]) |
&&((i_wb_ack)||(i_wb_err))) |
last_tag_valid <= 1'b0; |
|
// r_cache miss takes a clock cycle. It is only ever true for |
// something that should be cachable, but isn't in the cache. |
302,453 → 214,63
// Two clock path -- misses as well |
&&(r_rd)&&(!r_svalid) |
&&((r_itag != r_ctag)||(!r_iv)); |
end |
|
initial r_sel = 4'hf; |
always @(posedge i_clk) |
if (i_reset) |
r_sel <= 4'hf; |
else if (!o_pipe_stalled) |
begin |
casez({i_op[2:1], i_addr[1:0]}) |
4'b0???: r_sel <= 4'b1111; |
4'b100?: r_sel <= 4'b1100; |
4'b101?: r_sel <= 4'b0011; |
4'b1100: r_sel <= 4'b1000; |
4'b1101: r_sel <= 4'b0100; |
4'b1110: r_sel <= 4'b0010; |
4'b1111: r_sel <= 4'b0001; |
endcase |
r_rdata <= c_mem[r_addr[(CS-1):0]]; |
r_rvalid<= ((i_wb_ack)&&(last_ack)); |
end |
|
assign o_wb_sel = (state == DC_READC) ? 4'hf : r_sel; |
`define DC_IDLE 2'b00 |
`define DC_WRITE 2'b01 |
`define DC_READS 2'b10 |
`define DC_READC 2'b11 |
reg [1:0] state; |
|
initial o_wb_data = 0; |
reg [(AW-LS-1):0] wr_wtag, wr_vtag; |
reg [31:0] wr_data; |
reg [(CS-1):0] wr_addr; |
always @(posedge i_clk) |
if (i_reset) |
o_wb_data <= 0; |
else if ((!o_busy)||((stb)&&(!i_wb_stall))) |
begin |
casez(i_op[2:1]) |
2'b0?: o_wb_data <= i_data; |
2'b10: o_wb_data <= { (2){i_data[15:0]} }; |
2'b11: o_wb_data <= { (4){i_data[ 7:0]} }; |
endcase |
end |
|
generate if (OPT_PIPE) |
begin : OPT_PIPE_FIFO |
reg [NAUX+4-2:0] fifo_data [0:((1<<OPT_FIFO_DEPTH)-1)]; |
|
reg [DP:0] wraddr, rdaddr; |
|
always @(posedge i_clk) |
if (i_pipe_stb) |
fifo_data[wraddr[DP-1:0]] |
<= { i_oreg[NAUX-2:0], i_op[2:1], i_addr[1:0] }; |
|
always @(posedge i_clk) |
if (i_pipe_stb) |
gie <= i_oreg[NAUX-1]; |
|
`ifdef NO_BKRAM |
reg [NAUX+4-2:0] r_req_data, r_last_data; |
reg single_write; |
|
always @(posedge i_clk) |
r_req_data <= fifo_data[rdaddr[DP-1:0]]; |
|
always @(posedge i_clk) |
single_write <= (rdaddr == wraddr)&&(i_pipe_stb); |
|
always @(posedge i_clk) |
if (i_pipe_stb) |
r_last_data <= { i_oreg[NAUX-2:0], |
i_op[2:1], i_addr[1:0] }; |
|
always @(*) |
begin |
req_data[NAUX+4-1] = gie; |
// if ((r_svalid)||(state == DC_READ)) |
if (single_write) |
req_data[NAUX+4-2:0] = r_last_data; |
else |
req_data[NAUX+4-2:0] = r_req_data; |
end |
|
always @(*) |
`ASSERT(req_data == fifo_data[rdaddr[DP-1:0]]); |
`else |
always @(*) |
req_data[NAUX+4-2:0] = fifo_data[rdaddr[DP-1:0]]; |
always @(*) |
req_data[NAUX+4-1] = gie; |
`endif |
|
initial wraddr = 0; |
always @(posedge i_clk) |
if ((i_reset)||((cyc)&&(i_wb_err))) |
wraddr <= 0; |
else if (i_pipe_stb) |
wraddr <= wraddr + 1'b1; |
|
initial rdaddr = 0; |
always @(posedge i_clk) |
if ((i_reset)||((cyc)&&(i_wb_err))) |
rdaddr <= 0; |
else if ((r_dvalid)||(r_svalid)) |
rdaddr <= rdaddr + 1'b1; |
else if ((state == DC_WRITE)&&(i_wb_ack)) |
rdaddr <= rdaddr + 1'b1; |
else if ((state == DC_READS)&&(i_wb_ack)) |
rdaddr <= rdaddr + 1'b1; |
|
`ifdef FORMAL |
reg [AW-1:0] f_fifo_addr [0:((1<<OPT_FIFO_DEPTH)-1)]; |
reg [F_LGDEPTH-1:0] f_last_wraddr; |
reg f_pc_pending; |
|
always @(*) |
begin |
f_fill = 0; |
f_fill[DP:0] = wraddr - rdaddr; |
end |
|
always @(*) |
`ASSERT(f_fill <= { 1'b1, {(DP){1'b0}} }); |
|
always @(*) |
if ((r_dvalid)||(r_svalid)) |
begin |
if (r_svalid) |
`ASSERT(f_fill == 1); |
else if (r_dvalid) |
`ASSERT(f_fill == 1); |
else |
`ASSERT(f_fill == 0); |
end else if (r_rd_pending) |
`ASSERT(f_fill == 1); |
else |
`ASSERT(f_fill == npending); |
|
|
initial f_pc_pending = 0; |
always @(posedge i_clk) |
if (i_reset) |
f_pc_pending <= 1'b0; |
else if (i_pipe_stb) |
f_pc_pending <= (!i_op[0])&&(i_oreg[3:1] == 3'h7); |
else if (f_fill == 0) |
f_pc_pending <= 1'b0; |
//else if ((o_valid)&&(o_wreg[3:1] == 3'h7)&&(f_fill == 0)) |
// f_pc_pending <= 1'b0; |
|
always @(posedge i_clk) |
if (f_pc_pending) |
`ASSUME(!i_pipe_stb); |
|
always @(posedge i_clk) |
if (state == DC_WRITE) |
`ASSERT(!f_pc_pending); |
|
always @(*) |
begin |
f_last_wraddr = 0; |
f_last_wraddr[DP:0] = wraddr - 1'b1; |
end |
|
always @(posedge i_clk) |
if (r_rd_pending) |
`ASSERT(f_pc_pending == (fifo_data[f_last_wraddr][7:5] == 3'h7)); |
|
`define INSPECT_FIFO |
reg [((1<<(DP+1))-1):0] f_valid_fifo_entry; |
|
genvar k; |
for(k=0; k<(1<<(DP+1)); k=k+1) |
begin |
|
always @(*) |
begin |
f_valid_fifo_entry[k] = 1'b0; |
/* |
if ((rdaddr[DP] != wraddr[DP]) |
&&(rdaddr[DP-1:0] == wraddr[DP-1:0])) |
f_valid_fifo_entry[k] = 1'b1; |
else */ |
if ((rdaddr < wraddr)&&(k < wraddr) |
&&(k >= rdaddr)) |
f_valid_fifo_entry[k] = 1'b1; |
else if ((rdaddr > wraddr)&&(k >= rdaddr)) |
f_valid_fifo_entry[k] = 1'b1; |
else if ((rdaddr > wraddr)&&(k < wraddr)) |
f_valid_fifo_entry[k] = 1'b1; |
end |
|
`ifdef INSPECT_FIFO |
wire [NAUX+4-2:0] fifo_data_k; |
|
assign fifo_data_k = fifo_data[k[DP-1:0]]; |
always @(*) |
if (f_valid_fifo_entry[k]) |
begin |
if (!f_pc_pending) |
`ASSERT((o_wb_we)||(fifo_data_k[7:5] != 3'h7)); |
else if (k != f_last_wraddr) |
`ASSERT(fifo_data_k[7:5] != 3'h7); |
end |
`endif // INSPECT_FIFO |
|
end |
|
`ifndef INSPECT_FIFO |
always @(posedge i_clk) |
if ((r_rd_pending)&&(rdaddr[DP:0] != f_last_wraddr[DP-1])) |
assume(fifo_data[rdaddr][7:5] != 3'h7); |
`endif // INSPECT_FIFO |
|
assign f_pc = f_pc_pending; |
|
// |
// |
// |
always @(*) |
f_pending_addr = f_fifo_addr[rdaddr]; |
|
// |
// |
// |
always @(posedge i_clk) |
if (i_pipe_stb) |
f_fifo_addr[wraddr[DP-1:0]] <= i_addr[AW+1:2]; |
|
always @(*) |
begin |
f_return_address[AW] = (o_wb_cyc_lcl); |
f_return_address[AW-1:0] = f_fifo_addr[rdaddr]; |
if (state == DC_READC) |
f_return_address[LS-1:0] |
= (o_wb_addr[LS-1:0] - f_outstanding[LS-1:0]); |
end |
|
`define TWIN_WRITE_TEST |
`ifdef TWIN_WRITE_TEST |
(* anyconst *) reg [DP:0] f_twin_base; |
reg [DP:0] f_twin_next; |
(* anyconst *) reg [AW+NAUX+4-2-1:0] f_twin_first, |
f_twin_second; |
// reg [AW-1:0] f_fifo_addr [0:((1<<OPT_FIFO_DEPTH)-1)]; |
// reg [NAUX+4-2:0] fifo_data [0:((1<<OPT_FIFO_DEPTH)-1)]; |
|
always @(*) f_twin_next = f_twin_base+1; |
|
reg f_twin_none, f_twin_single, f_twin_double, f_twin_last; |
reg f_twin_valid_one, f_twin_valid_two; |
always @(*) |
begin |
f_twin_valid_one = ((f_valid_fifo_entry[f_twin_base]) |
&&(f_twin_first == { f_fifo_addr[f_twin_base], |
fifo_data[f_twin_base] })); |
f_twin_valid_two = ((f_valid_fifo_entry[f_twin_next]) |
&&(f_twin_second == { f_fifo_addr[f_twin_next], |
fifo_data[f_twin_next] })); |
end |
|
always @(*) |
begin |
f_twin_none =(!f_twin_valid_one)&&(!f_twin_valid_two); |
f_twin_single =( f_twin_valid_one)&&(!f_twin_valid_two); |
f_twin_double =( f_twin_valid_one)&&( f_twin_valid_two); |
f_twin_last =(!f_twin_valid_one)&&( f_twin_valid_two); |
end |
|
always @(posedge i_clk) |
if ((!f_past_valid)||($past(i_reset))||($past(cyc && i_wb_err))) |
`ASSERT(f_twin_none); |
else if ($past(f_twin_none)) |
`ASSERT(f_twin_none || f_twin_single || f_twin_last); |
else if ($past(f_twin_single)) |
`ASSERT(f_twin_none || f_twin_single || f_twin_double || f_twin_last); |
else if ($past(f_twin_double)) |
`ASSERT(f_twin_double || f_twin_last); |
else if ($past(f_twin_last)) |
`ASSERT(f_twin_none || f_twin_single || f_twin_last); |
|
`endif // TWIN_WRITE_TEST |
|
always @(*) |
`ASSERT(req_data == { gie, fifo_data[rdaddr[DP-1:0]] }); |
|
always @(posedge i_clk) |
if (r_svalid||r_dvalid || r_rd_pending) |
`ASSERT(f_fill == 1); |
else if (f_fill > 0) |
`ASSERT(cyc); |
|
always @(posedge i_clk) |
if (state != 0) |
`ASSERT(f_fill > 0); |
else if (!r_svalid && !r_dvalid && !r_rd_pending) |
`ASSERT(f_fill == 0); |
|
`endif // FORMAL |
|
always @(posedge i_clk) |
o_wreg <= req_data[(NAUX+4-1):4]; |
|
/* |
reg fifo_err; |
always @(posedge i_clk) |
begin |
fifo_err <= 1'b0; |
if ((!o_busy)&&(rdaddr != wraddr)) |
fifo_err <= 1'b1; |
if ((!r_dvalid)&&(!r_svalid)&&(!r_rd_pending)) |
fifo_err <= (npending != (wraddr-rdaddr)); |
end |
|
always @(*) |
o_debug = { i_pipe_stb, state, cyc, stb, // 5b |
fifo_err, i_oreg[3:0], o_wreg, // 10b |
rdaddr, wraddr, // 10b |
i_wb_ack, i_wb_err, o_pipe_stalled, o_busy,//4b |
r_svalid, r_dvalid, r_rd_pending }; |
*/ |
end else begin : NO_FIFO |
|
always @(posedge i_clk) |
if (i_pipe_stb) |
req_data <= { i_oreg, i_op[2:1], i_addr[1:0] }; |
|
always @(*) |
o_wreg = req_data[(NAUX+4-1):4]; |
|
always @(*) |
gie = i_oreg[NAUX-1]; |
|
`ifdef FORMAL |
assign f_pc = ((r_rd_pending)||(o_valid))&&(o_wreg[3:1] == 3'h7); |
|
// |
// |
// |
initial f_pending_addr = 0; |
always @(posedge i_clk) |
if (i_reset) |
f_pending_addr <= 0; |
else if (i_pipe_stb) |
begin |
f_pending_addr <= { (OPT_LOCAL_BUS)&&(&i_addr[DW-1:DW-8]), |
i_addr[(AW+1):2] }; |
end |
|
// |
// |
always @(*) |
begin |
f_return_address[AW] = o_wb_cyc_lcl; |
f_return_address[AW-1:LS] = o_wb_addr[AW-1:LS]; |
end |
always @(*) |
if (state == DC_READS) |
f_return_address[LS-1:0] = o_wb_addr[LS-1:0]; |
else |
f_return_address[LS-1:0] |
= (o_wb_addr[LS-1:0] - f_outstanding[LS-1:0]); |
|
`endif |
/* |
always @(*) |
o_debug = { i_pipe_stb, state, cyc, stb, // 5b |
i_oreg, o_wreg, // 10b |
10'hb, // 10b |
i_wb_ack, i_wb_err, o_pipe_stalled, o_busy,//4b |
r_svalid, r_dvalid, r_rd_pending }; |
*/ |
|
// verilator lint_off UNUSED |
wire unused_no_fifo; |
assign unused_no_fifo = gie; |
// verilator lint_on UNUSED |
end endgenerate |
|
|
initial r_wb_cyc_gbl = 0; |
initial r_wb_cyc_lcl = 0; |
initial o_wb_stb_gbl = 0; |
initial o_wb_stb_lcl = 0; |
initial c_v = 0; |
initial cyc = 0; |
initial stb = 0; |
initial c_wr = 0; |
initial wr_cstb = 0; |
initial state = DC_IDLE; |
initial set_vflag = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
begin |
c_v <= 0; |
c_wr <= 1'b0; |
c_wsel <= 4'hf; |
r_wb_cyc_gbl <= 1'b0; |
r_wb_cyc_lcl <= 1'b0; |
o_wb_stb_gbl <= 0; |
o_wb_stb_lcl <= 0; |
wr_cstb <= 1'b0; |
last_line_stb <= 1'b0; |
end_of_line <= 1'b0; |
state <= DC_IDLE; |
cyc <= 1'b0; |
stb <= 1'b0; |
state <= DC_IDLE; |
set_vflag <= 1'b0; |
end else begin |
// By default, update the cache from the write 1-clock ago |
// c_wr <= (wr_cstb)&&(wr_wtag == wr_vtag); |
// c_waddr <= wr_addr[(CS-1):0]; |
c_wr <= 0; |
c_wr <= (wr_cstb)&&(wr_wtag == wr_vtag); |
c_wdata <= wr_data; |
c_waddr <= wr_addr[(CS-1):0]; |
|
set_vflag <= 1'b0; |
if ((!cyc)&&(set_vflag)) |
c_v[c_waddr[(CS-1):LS]] <= 1'b1; |
|
wr_cstb <= 1'b0; |
wr_vtag <= c_vtags[o_wb_addr[(CS-LS-1):0]]; |
wr_wtag <= o_wb_addr[(AW-LS-1):0]; |
wr_data <= o_wb_data; |
wr_addr <= o_wb_addr[(CS-1):0]; |
|
if (!cyc) |
wr_addr <= r_addr[(CS-1):0]; |
else if (i_wb_ack) |
wr_addr <= wr_addr + 1'b1; |
else |
wr_addr <= wr_addr; |
|
if (LS <= 0) |
if (LS <= 1) |
end_of_line <= 1'b1; |
else if (!cyc) |
end_of_line <= 1'b0; |
else if (!end_of_line) |
begin |
if (i_wb_ack) |
end_of_line |
<= (c_waddr[(LS-1):0] == {{(LS-2){1'b1}},2'b01}); |
else |
end_of_line |
<= (c_waddr[(LS-1):0]=={{(LS-1){1'b1}}, 1'b0}); |
end |
else |
end_of_line<=(cyc)&&((c_waddr[(LS-1):1]=={(LS-1){1'b1}}) |
||((i_wb_ack) |
&&(c_waddr[(LS-1):0]=={{(LS-2){1'b1}},2'b01}))); |
|
if (!cyc) |
last_line_stb <= (LS <= 0); |
else if ((stb)&&(!i_wb_stall)&&(LS <= 1)) |
if (LS <= 1) |
last_line_stb <= 1'b1; |
else if ((stb)&&(!i_wb_stall)) |
last_line_stb <= (o_wb_addr[(LS-1):1]=={(LS-1){1'b1}}); |
else if (stb) |
last_line_stb <= (o_wb_addr[(LS-1):0]=={(LS){1'b1}}); |
else |
last_line_stb <= (stb)&& |
((o_wb_addr[(LS-1):1]=={(LS-1){1'b1}}) |
||((!i_wb_stall) |
&&(o_wb_addr[(LS-1):0] |
=={{(LS-2){1'b1}},2'b01}))); |
|
// |
// |
if (state == DC_IDLE) |
if (state == `DC_IDLE) |
pipeable_op <= 1'b0; |
if (state == `DC_IDLE) |
non_pipeable_op <= 1'b0; |
|
|
if (state == `DC_IDLE) |
begin |
o_wb_we <= 1'b0; |
o_wb_data <= i_data; |
pipeable_op <= 1'b0; |
non_pipeable_op <= 1'b1; |
|
cyc <= 1'b0; |
stb <= 1'b0; |
758,58 → 280,47
o_wb_stb_gbl <= 1'b0; |
o_wb_stb_lcl <= 1'b0; |
|
in_cache <= (i_op[0])&&(w_cachable); |
if ((i_pipe_stb)&&(i_op[0])) |
in_cache <= (i_op)&&(w_cachable); |
if ((i_pipe_stb)&&(i_op)) |
begin // Write operation |
state <= DC_WRITE; |
o_wb_addr <= i_addr[(AW+1):2]; |
state <= `DC_WRITE; |
o_wb_addr <= i_addr; |
o_wb_we <= 1'b1; |
pipeable_op <= 1'b1; |
|
cyc <= 1'b1; |
stb <= 1'b1; |
|
if (OPT_LOCAL_BUS) |
begin |
r_wb_cyc_gbl <= (i_addr[DW-1:DW-8]!=8'hff); |
r_wb_cyc_lcl <= (i_addr[DW-1:DW-8]==8'hff); |
o_wb_stb_gbl <= (i_addr[DW-1:DW-8]!=8'hff); |
o_wb_stb_lcl <= (i_addr[DW-1:DW-8]==8'hff); |
end else begin |
r_wb_cyc_gbl <= 1'b1; |
o_wb_stb_gbl <= 1'b1; |
end |
r_wb_cyc_gbl <= (i_addr[31:30]!=2'b11); |
r_wb_cyc_lcl <= (i_addr[31:30]==2'b11); |
o_wb_stb_gbl <= (i_addr[31:30]!=2'b11); |
o_wb_stb_lcl <= (i_addr[31:30]==2'b11); |
|
end else if (r_cache_miss) |
begin |
state <= DC_READC; |
o_wb_addr <= { r_ctag, {(LS){1'b0}} }; |
state <= `DC_READC; |
o_wb_addr <= { i_ctag, {(LS){1'b0}} }; |
non_pipeable_op <= 1'b1; |
|
c_waddr <= { r_ctag[CS-LS-1:0], {(LS){1'b0}} }-1'b1; |
cyc <= 1'b1; |
stb <= 1'b1; |
r_wb_cyc_gbl <= 1'b1; |
o_wb_stb_gbl <= 1'b1; |
wr_addr[LS-1:0] <= 0; |
end else if ((i_pipe_stb)&&(!w_cachable)) |
begin // Read non-cachable memory area |
state <= DC_READS; |
o_wb_addr <= i_addr[(AW+1):2]; |
state <= `DC_READS; |
o_wb_addr <= i_addr; |
pipeable_op <= 1'b1; |
|
cyc <= 1'b1; |
stb <= 1'b1; |
if (OPT_LOCAL_BUS) |
begin |
r_wb_cyc_gbl <= (i_addr[DW-1:DW-8]!=8'hff); |
r_wb_cyc_lcl <= (i_addr[DW-1:DW-8]==8'hff); |
o_wb_stb_gbl <= (i_addr[DW-1:DW-8]!=8'hff); |
o_wb_stb_lcl <= (i_addr[DW-1:DW-8]==8'hff); |
end else begin |
r_wb_cyc_gbl <= 1'b1; |
o_wb_stb_gbl <= 1'b1; |
end |
r_wb_cyc_gbl <= (i_addr[31:30]!=2'b11); |
r_wb_cyc_lcl <= (i_addr[31:30]==2'b11); |
o_wb_stb_gbl <= (i_addr[31:30]!=2'b11); |
o_wb_stb_lcl <= (i_addr[31:30]==2'b11); |
end // else we stay idle |
|
end else if (state == DC_READC) |
end else if (state == `DC_READC) |
begin |
// We enter here once we have committed to reading |
// data into a cache line. |
820,31 → 331,26
o_wb_addr[(LS-1):0] <= o_wb_addr[(LS-1):0]+1'b1; |
end |
|
if ((i_wb_ack)&&(!end_of_line)) |
c_v[o_wb_addr[(CS-1):LS]] <= 1'b0; |
if(stb) |
c_v[o_wb_addr[(CS-LS-1):0]] <= 1'b0; |
|
c_wr <= (i_wb_ack); |
c_wdata <= i_wb_data; |
c_waddr <= ((i_wb_ack)?(c_waddr+1'b1):c_waddr); |
c_wsel <= 4'hf; |
c_wr <= (i_wb_ack); |
c_wdata <= o_wb_data; |
c_waddr <= ((c_wr)?(c_waddr+1'b1):c_waddr); |
|
set_vflag <= !i_wb_err; |
if (i_wb_ack) |
c_vtags[r_addr[(CS-1):LS]] |
<= r_addr[(AW-1):LS]; |
c_vtags[o_wb_addr[(CS-LS-1):0]]<= o_wb_addr[(AW-LS-1):0]; |
|
if (((i_wb_ack)&&(end_of_line))||(i_wb_err)) |
begin |
state <= DC_IDLE; |
state <= `DC_IDLE; |
non_pipeable_op <= 1'b0; |
cyc <= 1'b0; |
stb <= 1'b0; |
r_wb_cyc_gbl <= 1'b0; |
r_wb_cyc_lcl <= 1'b0; |
o_wb_stb_gbl <= 1'b0; |
o_wb_stb_lcl <= 1'b0; |
// |
c_v[o_wb_addr[(CS-LS-1):0]] <= i_wb_ack; |
end |
end else if (state == DC_READS) |
end else if (state == `DC_READS) |
begin |
// We enter here once we have committed to reading |
// data that cannot go into a cache line |
853,31 → 359,27
stb <= 1'b0; |
o_wb_stb_gbl <= 1'b0; |
o_wb_stb_lcl <= 1'b0; |
pipeable_op <= 1'b0; |
end |
|
if ((!i_wb_stall)&&(i_pipe_stb)) |
o_wb_addr <= i_addr[(AW+1):2]; |
o_wb_addr <= i_data; |
|
c_wr <= 1'b0; |
|
if (((i_wb_ack)&&(last_ack))||(i_wb_err)) |
begin |
state <= DC_IDLE; |
state <= `DC_IDLE; |
cyc <= 1'b0; |
stb <= 1'b0; |
r_wb_cyc_gbl <= 1'b0; |
r_wb_cyc_lcl <= 1'b0; |
o_wb_stb_gbl <= 1'b0; |
o_wb_stb_lcl <= 1'b0; |
end |
end else if (state == DC_WRITE) |
end else if (state == `DC_WRITE) |
begin |
c_wr <= (stb)&&(c_v[o_wb_addr[CS-1:LS]]) |
&&(c_vtags[o_wb_addr[CS-1:LS]]==o_wb_addr[AW-1:LS]) |
&&(stb); |
// c_wr <= (c_v[])&&(c_tag[])&&(in_cache)&&(stb); |
c_wdata <= o_wb_data; |
c_waddr <= r_addr[CS-1:0]; |
c_wsel <= o_wb_sel; |
c_waddr <= (state == `DC_IDLE)?i_caddr |
: ((c_wr)?(c_waddr+1'b1):c_waddr); |
|
if ((!i_wb_stall)&&(!i_pipe_stb)) |
begin |
884,79 → 386,27
stb <= 1'b0; |
o_wb_stb_gbl <= 1'b0; |
o_wb_stb_lcl <= 1'b0; |
pipeable_op <= 1'b0; |
end |
|
wr_cstb <= (stb)&&(!i_wb_stall)&&(in_cache); |
|
if ((stb)&&(!i_wb_stall)) |
o_wb_addr <= i_addr[(AW+1):2]; |
|
if (((i_wb_ack)&&(last_ack) |
&&((!OPT_PIPE)||(!i_pipe_stb))) |
||(i_wb_err)) |
if ((stb)&&(!i_wb_stall)&&(i_pipe_stb)) |
o_wb_addr <= i_addr; |
if ((stb)&&(!i_wb_stall)&&(i_pipe_stb)) |
o_wb_data <= i_data; |
|
if (((i_wb_ack)&&(last_ack))||(i_wb_err)) |
begin |
state <= DC_IDLE; |
state <= `DC_IDLE; |
cyc <= 1'b0; |
stb <= 1'b0; |
r_wb_cyc_gbl <= 1'b0; |
r_wb_cyc_lcl <= 1'b0; |
o_wb_stb_gbl <= 1'b0; |
o_wb_stb_lcl <= 1'b0; |
end |
end |
end |
|
// |
// npending is the number of outstanding (non-cached) read or write |
// requests |
initial npending = 0; |
always @(posedge i_clk) |
if ((i_reset)||(!OPT_PIPE) |
||((cyc)&&(i_wb_err)) |
||((!cyc)&&(!i_pipe_stb)) |
||(state == DC_READC)) |
npending <= 0; |
else if (r_svalid) |
npending <= (i_pipe_stb) ? 1:0; |
else case({ (i_pipe_stb), (cyc)&&(i_wb_ack) }) |
2'b01: npending <= npending - 1'b1; |
2'b10: npending <= npending + 1'b1; |
default: begin end |
endcase |
|
initial last_ack = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
last_ack <= 1'b0; |
else if (state == DC_IDLE) |
begin |
last_ack <= 1'b0; |
if ((i_pipe_stb)&&(i_op[0])) |
last_ack <= 1'b1; |
else if (r_cache_miss) |
last_ack <= (LS == 0); |
else if ((i_pipe_stb)&&(!w_cachable)) |
last_ack <= 1'b1; |
end else if (state == DC_READC) |
begin |
if (i_wb_ack) |
last_ack <= last_ack || (&wr_addr[LS-1:1]); |
else |
last_ack <= last_ack || (&wr_addr[LS-1:0]); |
end else case({ (i_pipe_stb), (i_wb_ack) }) |
2'b01: last_ack <= (npending <= 2); |
2'b10: last_ack <= (!cyc)||(npending == 0); |
default: begin end |
endcase |
|
`ifdef FORMAL |
always @(*) |
`ASSERT(npending <= { 1'b1, {(DP){1'b0}} }); |
|
`endif |
|
|
// |
// Writes to the cache |
// |
// These have been made as simple as possible. Note that the c_wr |
963,19 → 413,10
// line has already been determined, as have the write value and address |
// on the last clock. Further, this structure is defined to match the |
// block RAM design of as many architectures as possible. |
// |
// |
always @(posedge i_clk) |
if (c_wr) |
begin |
if (c_wsel[0]) |
c_mem[c_waddr][7:0] <= c_wdata[7:0]; |
if (c_wsel[1]) |
c_mem[c_waddr][15:8] <= c_wdata[15:8]; |
if (c_wsel[2]) |
c_mem[c_waddr][23:16] <= c_wdata[23:16]; |
if (c_wsel[3]) |
c_mem[c_waddr][31:24] <= c_wdata[31:24]; |
end |
if (c_wr) |
c_mem[c_waddr] <= c_wdata; |
|
// |
// Reads from the cache |
985,1101 → 426,82
// going to be our output will need to be determined with combinatorial |
// logic on the output. |
// |
generate if (OPT_DUAL_READ_PORT) |
begin |
reg [31:0] cached_idata, cached_rdata; |
always @(posedge i_clk) |
cached_idata <= c_mem[i_caddr]; |
|
always @(posedge i_clk) |
cached_idata <= c_mem[i_caddr]; |
always @(posedge i_clk) |
cached_rdata <= c_mem[r_caddr]; |
|
always @(posedge i_clk) |
cached_rdata <= c_mem[r_caddr]; |
|
end else begin |
|
always @(posedge i_clk) |
cached_rdata <= c_mem[(o_busy) ? r_caddr : i_caddr]; |
|
always @(*) |
cached_idata = cached_rdata; |
|
end endgenerate |
|
// o_data can come from one of three places: |
// 1. The cache, assuming the data was in the last cache line |
// 2. The cache, second clock, assuming the data was in the cache at all |
// 3. The cache, after filling the cache |
// 4. The wishbone state machine, upon reading the value desired. |
always @(*) |
always @(posedge i_clk) |
if (r_svalid) |
pre_data = cached_idata; |
else if (state == DC_READS) |
pre_data = i_wb_data; |
o_data <= cached_idata; |
else if ((i_wb_ack)&&(pipeable_op)) |
o_data <= i_wb_data; |
else |
pre_data = cached_rdata; |
|
o_data <= cached_rdata; |
always @(posedge i_clk) |
casez(req_data[3:0]) |
4'b100?: o_data <= { 16'h0, pre_data[31:16] }; |
4'b101?: o_data <= { 16'h0, pre_data[15: 0] }; |
4'b1100: o_data <= { 24'h0, pre_data[31:24] }; |
4'b1101: o_data <= { 24'h0, pre_data[23:16] }; |
4'b1110: o_data <= { 24'h0, pre_data[15: 8] }; |
4'b1111: o_data <= { 24'h0, pre_data[ 7: 0] }; |
default o_data <= pre_data; |
endcase |
|
initial o_valid = 1'b0; |
o_valid <= (r_svalid)||((i_wb_ack)&&(pipeable_op)) |
||(r_dvalid)||(r_rvalid); |
always @(posedge i_clk) |
if (i_reset) |
o_valid <= 1'b0; |
else if (state == DC_READS) |
o_valid <= i_wb_ack; |
else |
o_valid <= (r_svalid)||(r_dvalid); |
|
initial o_err = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
o_err <= 1'b0; |
else |
o_err <= (cyc)&&(i_wb_err); |
|
initial o_busy = 0; |
always @(posedge i_clk) |
if ((i_reset)||((cyc)&&(i_wb_err))) |
o_busy <= 1'b0; |
else if (i_pipe_stb) |
o_busy <= 1'b1; |
else if ((state == DC_READS)&&(i_wb_ack)) |
o_busy <= 1'b0; |
else if ((r_rd_pending)&&(!r_dvalid)) |
o_busy <= 1'b1; |
else if ((state == DC_WRITE) |
&&(i_wb_ack)&&(last_ack)&&(!i_pipe_stb)) |
o_busy <= 1'b0; |
else if (cyc) |
o_busy <= 1'b1; |
else // if ((r_dvalid)||(r_svalid)) |
o_busy <= 1'b0; |
assign o_busy = (state != `DC_IDLE); |
|
// |
// We can use our FIFO addresses to pre-calculate when an ACK is going |
// to be the last_noncachable_ack. |
|
|
always @(*) |
if (OPT_PIPE) |
o_pipe_stalled = (cyc)&&((!o_wb_we)||(i_wb_stall)||(!stb)) |
||(r_rd_pending)||(npending[DP]); |
else |
o_pipe_stalled = o_busy; |
|
initial lock_gbl = 0; |
initial lock_lcl = 0; |
always @(posedge i_clk) |
if (i_reset) |
begin |
lock_gbl <= 1'b0; |
lock_lcl<= 1'b0; |
end else begin |
lock_gbl <= (OPT_LOCK)&&(i_lock)&&((r_wb_cyc_gbl)||(lock_gbl)); |
lock_lcl <= (OPT_LOCK)&&(i_lock)&&((r_wb_cyc_lcl)||(lock_lcl)); |
end |
|
assign o_wb_cyc_gbl = (r_wb_cyc_gbl)||(lock_gbl); |
assign o_wb_cyc_lcl = (r_wb_cyc_lcl)||(lock_lcl); |
|
generate if (AW+2 < DW) |
begin : UNUSED_BITS |
|
// Verilator lint_off UNUSED |
wire [DW-AW-2:0] unused; |
assign unused = i_addr[DW-1:AW+1]; |
// Verilator lint_on UNUSED |
end endgenerate |
|
`ifdef FORMAL |
|
initial f_past_valid = 1'b0; |
always @(posedge i_clk) |
f_past_valid <= 1'b1; |
|
//////////////////////////////////////////////// |
// |
// Reset properties |
// Handle our auxilliary data lines. |
// |
//////////////////////////////////////////////// |
// These just go into a FIFO upon request, and then get fed back out |
// upon completion of an OP. |
// |
// These are currently designed for handling bursts of writes or |
// non-cachable reads. |
// |
always @(*) |
if(!f_past_valid) |
`ASSUME(i_reset); |
|
always @(posedge i_clk) |
if ((!f_past_valid)||($past(i_reset))) |
begin |
// Insist on initial statements matching reset values |
`ASSERT(r_rd == 1'b0); |
`ASSERT(r_cachable == 1'b0); |
`ASSERT(r_svalid == 1'b0); |
`ASSERT(r_dvalid == 1'b0); |
`ASSERT(r_cache_miss == 1'b0); |
`ASSERT(r_addr == 0); |
// |
`ASSERT(c_wr == 0); |
`ASSERT(c_v == 0); |
// |
// assert(aux_head == 0); |
// assert(aux_tail == 0); |
// |
`ASSERT(lock_gbl == 0); |
`ASSERT(lock_lcl == 0); |
end |
|
//////////////////////////////////////////////// |
// A very similar structure will be used once we switch to using an |
// MMU, in order to make certain memory operations are synchronous |
// enough to deal with bus errors. |
// |
// Assumptions about our inputs |
// |
//////////////////////////////////////////////// |
// |
// |
always @(*) |
if (o_pipe_stalled) |
`ASSUME(!i_pipe_stb); |
|
always @(*) |
if (!f_past_valid) |
`ASSUME(!i_pipe_stb); |
|
reg [(LGAUX-1):0] aux_head, aux_tail; |
reg [(NAUX-1):0] aux_fifo [0:((1<<LGAUX)-1)]; |
initial aux_head = 0; |
initial aux_tail = 0; |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset)) |
&&($past(i_pipe_stb))&&($past(o_pipe_stalled))) |
begin |
`ASSUME($stable(i_pipe_stb)); |
`ASSUME($stable(i_op[0])); |
`ASSUME($stable(i_addr)); |
if (i_op[0]) |
`ASSUME($stable(i_data)); |
if ((i_rst)||(i_wb_err)) |
aux_head <= 0; |
else if ((i_pipe_stb)&&(!o_busy)) |
aux_head <= aux_head + 1'b1; |
aux_fifo[aux_head] <= i_oreg; |
end |
|
always @(posedge i_clk) |
if (o_err) |
`ASSUME(!i_pipe_stb); |
|
//////////////////////////////////////////////// |
// |
// Wishbone properties |
// |
//////////////////////////////////////////////// |
// |
// |
wire f_cyc, f_stb; |
|
assign f_cyc = (o_wb_cyc_gbl)|(o_wb_cyc_lcl); |
assign f_stb = (o_wb_stb_gbl)|(o_wb_stb_lcl); |
|
always @(*) |
begin |
// Only one interface can be active at once |
`ASSERT((!o_wb_cyc_gbl)||(!o_wb_cyc_lcl)); |
// Strobe may only be active on the active interface |
`ASSERT((r_wb_cyc_gbl)||(!o_wb_stb_gbl)); |
`ASSERT((r_wb_cyc_lcl)||(!o_wb_stb_lcl)); |
if (o_wb_stb_lcl) |
begin |
if (o_wb_we) |
assert(state == DC_WRITE); |
else |
assert(state == DC_READS); |
end |
|
if (cyc) |
assert(o_wb_we == (state == DC_WRITE)); |
if ((i_rst)||(i_wb_err)) |
aux_tail <= 0; |
else if (o_valid) // ||(aux_tail[WBIT])&&(no-mmu-error) |
aux_tail <= aux_tail + 1'b1; |
o_wreg <= aux_fifo[aux_tail]; |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(cyc)&&($past(cyc))) |
begin |
`ASSERT($stable(r_wb_cyc_gbl)); |
`ASSERT($stable(r_wb_cyc_lcl)); |
end |
|
|
`ifdef DCACHE |
`define FWB_MASTER fwb_master |
`else |
`define FWB_MASTER fwb_counter |
`endif |
|
`FWB_MASTER #( |
.AW(AW), .DW(DW), |
.F_MAX_STALL(2), |
.F_MAX_ACK_DELAY(3), |
// If you need the proof to run faster, use these |
// lines instead of the two that follow |
// .F_MAX_STALL(1), |
// .F_MAX_ACK_DELAY(1), |
.F_LGDEPTH(F_LGDEPTH), |
.F_MAX_REQUESTS((OPT_PIPE) ? 0 : (1<<LS)), |
`ifdef DCACHE |
.F_OPT_SOURCE(1'b1), |
`endif |
.F_OPT_DISCONTINUOUS(0) |
) fwb(i_clk, i_reset, |
cyc, f_stb, o_wb_we, o_wb_addr, o_wb_data, o_wb_sel, |
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err, |
f_nreqs, f_nacks, f_outstanding); |
|
`ifdef DCACHE // Arbitrary access is specific to local dcache implementation |
//////////////////////////////////////////////// |
// |
// Arbitrary address properties |
// |
//////////////////////////////////////////////// |
// |
// |
(* anyconst *) reg [AW:0] f_const_addr; |
(* anyconst *) reg f_const_buserr; |
wire [AW-LS-1:0] f_const_tag, f_ctag_here, f_wb_tag; |
wire [CS-LS-1:0] f_const_tag_addr; |
reg [DW-1:0] f_const_data; |
wire [DW-1:0] f_cmem_here; |
reg f_pending_rd; |
wire f_cval_in_cache; |
// We can use our FIFO addresses to pre-calculate when an ACK is going |
// to be the last_noncachable_ack. |
|
assign f_const_tag = f_const_addr[AW-1:LS]; |
assign f_const_tag_addr = f_const_addr[CS-1:LS]; |
assign f_cmem_here = c_mem[f_const_addr[CS-1:0]]; |
assign f_ctag_here = c_vtags[f_const_addr[CS-1:LS]]; |
assign f_wb_tag = o_wb_addr[AW-1:LS]; |
|
assign f_cval_in_cache= (c_v[f_const_addr[CS-1:LS]]) |
&&(f_ctag_here == f_const_tag); |
assign o_pipe_stalled=((pipeable_op)&&(i_wb_stall))||(non_pipeable_op); |
// pipeable_op must become zero when stb goes low |
|
generate if ((AW > DW - 8)&&(OPT_LOCAL_BUS)) |
begin : UPPER_CONST_ADDR_BITS |
|
always @(*) |
if (f_const_addr[AW]) |
assume(&f_const_addr[(AW-1):(DW-8)]); |
else |
assume(!(&f_const_addr[(AW-1):(DW-8)])); |
end endgenerate |
|
wire [AW-1:0] wb_start; |
assign wb_start = (f_stb) ? (o_wb_addr - f_nreqs) : o_wb_addr; |
|
// Data changes upon request |
always @(posedge i_clk) |
begin |
if ((i_pipe_stb)&&(i_addr[(AW+1):2] == f_const_addr[AW-1:0]) |
&&(f_const_addr[AW] == ((OPT_LOCAL_BUS) |
&&(&i_addr[(DW-1):(DW-8)]))) |
&&(i_op[0])) |
begin |
casez({ i_op[2:1], i_addr[1:0] }) |
4'b0???: f_const_data <= i_data; |
4'b100?: f_const_data[31:16] <= i_data[15:0]; |
4'b101?: f_const_data[15: 0] <= i_data[15:0]; |
4'b1100: f_const_data[31:24] <= i_data[ 7:0]; |
4'b1101: f_const_data[23:16] <= i_data[ 7:0]; |
4'b1110: f_const_data[15: 8] <= i_data[ 7:0]; |
4'b1111: f_const_data[ 7: 0] <= i_data[ 7:0]; |
endcase |
end |
|
if (f_cval_in_cache) |
assume((!i_wb_err) |
||(!i_pipe_stb) |
||(f_const_addr[AW-1:0] != i_addr[AW+1:2])); |
lock_gbl <= (i_lock)&&((r_wb_cyc_gbl)||(lock_gbl)); |
lock_lcl <= (i_lock)&&((r_wb_cyc_lcl)||(lock_lcl)); |
end |
|
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!i_reset)&&(!f_const_buserr)) |
begin |
if ((cyc)&&(o_wb_we)&&(f_stb) |
&&(o_wb_addr[AW-1:0] == f_const_addr[AW-1:0]) |
&&( o_wb_stb_lcl == f_const_addr[AW])) |
begin |
|
// |
// Changing our data |
// |
if (o_wb_sel[0]) |
`ASSERT(o_wb_data[ 7: 0]==f_const_data[ 7: 0]); |
if (o_wb_sel[1]) |
`ASSERT(o_wb_data[15: 8]==f_const_data[15: 8]); |
if (o_wb_sel[2]) |
`ASSERT(o_wb_data[23:16]==f_const_data[23:16]); |
if (o_wb_sel[3]) |
`ASSERT(o_wb_data[31:24]==f_const_data[31:24]); |
|
// Check the data in the cache |
if ((!f_const_addr[AW])&&(c_v[f_const_tag_addr]) |
&&(f_ctag_here == o_wb_addr[AW-1:LS])) |
begin |
if ((!c_wsel[0])&&(!o_wb_sel[0])) |
`ASSERT(f_cmem_here[ 7: 0]==f_const_data[ 7: 0]); |
if ((!c_wsel[1])&&(!o_wb_sel[1])) |
`ASSERT(f_cmem_here[15: 8]==f_const_data[15: 8]); |
if ((!c_wsel[2])&&(!o_wb_sel[2])) |
`ASSERT(f_cmem_here[23:16]==f_const_data[23:16]); |
if ((!c_wsel[3])&&(!o_wb_sel[3])) |
`ASSERT(f_cmem_here[31:24]==f_const_data[31:24]); |
|
end |
end else if ((!f_const_addr[AW])&&(c_v[f_const_tag_addr]) |
&&(f_ctag_here ==f_const_addr[AW-1:LS])) |
begin |
// If ... |
// 1. Our magic address is cachable |
// 2. Our magic address is associated with a valid |
// cache line |
// 3. The cache tag matches our magic address |
|
|
// if ($past(cyc && i_wb_err)) |
// begin |
// Ignore what happens on an error, the result |
// becomes undefined anyway |
// end else |
if ((c_wr) |
&&(c_waddr[CS-1:0] == f_const_addr[CS-1:0])) |
begin |
// |
// If we are writing to this valid cache line |
// |
if (c_wsel[3]) |
`ASSERT(c_wdata[31:24] |
== f_const_data[31:24]); |
else |
`ASSERT(f_cmem_here[31:24] |
== f_const_data[31:24]); |
if (c_wsel[2]) |
`ASSERT(c_wdata[23:16] |
== f_const_data[23:16]); |
else |
`ASSERT(f_cmem_here[23:16] == f_const_data[23:16]); |
if (c_wsel[1]) |
`ASSERT(c_wdata[15:8] |
== f_const_data[15:8]); |
else |
`ASSERT(f_cmem_here[15:8] == f_const_data[15:8]); |
if (c_wsel[0]) |
`ASSERT(c_wdata[7:0] |
== f_const_data[7:0]); |
else |
`ASSERT(f_cmem_here[7:0] == f_const_data[7:0]); |
end else |
`ASSERT(f_cmem_here == f_const_data); |
end |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(state == DC_READC)) |
begin |
`ASSERT(f_wb_tag == r_ctag); |
if ((wb_start[AW-1:LS] == f_const_tag) |
&&(!c_v[f_const_tag_addr]) |
&&(f_const_addr[AW] == r_wb_cyc_lcl) |
&&(f_nacks > f_const_addr[LS-1:0])) |
begin |
// We are reading the cache line containing our |
// constant address f_const_addr. Make sure the data |
// is correct. |
if ((c_wr)&&(c_waddr[CS-1:0] == f_const_addr[CS-1:0])) |
`ASSERT(c_wdata == f_const_data); |
else |
`ASSERT(f_cmem_here == f_const_data); |
end |
|
if (f_nacks > 0) |
`ASSERT(!c_v[wb_start[CS-1:LS]]); |
end |
|
always @(posedge i_clk) |
if ((state == DC_READC)&&(f_nacks > 0)) |
begin |
`ASSERT(c_vtags[wb_start[(CS-1):LS]] <= wb_start[(AW-1):LS]); |
`ASSERT(c_vtags[wb_start[(CS-1):LS]] <= r_addr[AW-1:LS]); |
end |
|
reg [AW-1:0] f_cache_waddr; |
wire f_this_cache_waddr; |
|
always @(*) |
begin |
// f_cache_waddr[AW-1:LS] = c_vtags[c_waddr[CS-1:CS-LS]]; |
f_cache_waddr[AW-1:LS] = wb_start[AW-1:LS]; |
f_cache_waddr[CS-1: 0] = c_waddr[CS-1:0]; |
end |
|
assign f_this_cache_waddr = (!f_const_addr[AW]) |
&&(f_cache_waddr == f_const_addr[AW-1:0]); |
always @(posedge i_clk) |
if ((f_past_valid)&&(state == DC_READC)) |
begin |
if ((c_wr)&&(c_waddr[LS-1:0] != 0)&&(f_this_cache_waddr)) |
`ASSERT(c_wdata == f_const_data); |
end |
|
always @(posedge i_clk) |
if ((OPT_PIPE)&&(o_busy)&&(i_pipe_stb)) |
begin |
`ASSUME(i_op[0] == o_wb_we); |
if (o_wb_cyc_lcl) |
assume(&i_addr[DW-1:DW-8]); |
else |
assume(!(&i_addr[DW-1:DW-8])); |
end |
|
initial f_pending_rd = 0; |
always @(posedge i_clk) |
if (i_reset) |
f_pending_rd <= 0; |
else if (i_pipe_stb) |
f_pending_rd <= (!i_op[0]); |
else if ((o_valid)&&((!OPT_PIPE) |
||((state != DC_READS)&&(!r_svalid)&&(!$past(i_pipe_stb))))) |
f_pending_rd <= 1'b0; |
|
always @(*) |
if ((state == DC_READC)&&(!f_stb)) |
`ASSERT(f_nreqs == (1<<LS)); |
|
always @(*) |
if ((state == DC_READC)&&(f_stb)) |
`ASSERT(f_nreqs == { 1'b0, o_wb_addr[LS-1:0] }); |
|
always @(posedge i_clk) |
if (state == DC_READC) |
begin |
if (($past(i_wb_ack))&&(!$past(f_stb))) |
`ASSERT(f_nacks-1 == { 1'b0, c_waddr[LS-1:0] }); |
else if (f_nacks > 0) |
begin |
`ASSERT(f_nacks-1 == { 1'b0, c_waddr[LS-1:0] }); |
`ASSERT(c_waddr[CS-1:LS] == o_wb_addr[CS-1:LS]); |
end else begin |
`ASSERT(c_waddr[CS-1:LS] == o_wb_addr[CS-1:LS]-1'b1); |
`ASSERT(&c_waddr[LS-1:0]); |
end |
end |
|
always @(*) |
if (r_rd_pending) |
`ASSERT(r_addr == f_pending_addr[AW-1:0]); |
|
always @(*) |
if (f_pending_addr[AW]) |
begin |
`ASSERT(state != DC_READC); |
`ASSERT((!o_wb_we)||(!o_wb_cyc_gbl)); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(o_valid)&&($past(f_pending_addr) == f_const_addr)) |
begin |
if (f_const_buserr) |
`ASSERT(o_err); |
else if (f_pending_rd) |
begin |
casez($past(req_data[3:0])) |
4'b0???: `ASSERT(o_data ==f_const_data); |
4'b101?: `ASSERT(o_data =={16'h00,f_const_data[15: 0]}); |
4'b100?: `ASSERT(o_data =={16'h00,f_const_data[31:16]}); |
4'b1100: `ASSERT(o_data =={24'h00,f_const_data[31:24]}); |
4'b1101: `ASSERT(o_data =={24'h00,f_const_data[23:16]}); |
4'b1110: `ASSERT(o_data =={24'h00,f_const_data[15: 8]}); |
4'b1111: `ASSERT(o_data =={24'h00,f_const_data[ 7: 0]}); |
endcase |
end |
end |
|
wire f_this_return; |
|
assign f_this_return = (f_return_address == f_const_addr); |
always @(*) |
if ((f_cyc)&&( |
((state == DC_READC) |
&&(f_return_address[AW-1:LS] == f_const_addr[AW-1:LS])) |
||(f_this_return))&&(f_cyc)) |
begin |
if (f_const_buserr) |
assume(!i_wb_ack); |
else begin |
assume(!i_wb_err); |
assume(i_wb_data == f_const_data); |
end |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(last_tag == f_const_tag)&&(f_const_buserr) |
&&(!f_const_addr[AW])) |
`ASSERT(!last_tag_valid); |
|
always @(*) |
if (f_const_buserr) |
begin |
`ASSERT((!c_v[f_const_tag_addr])||(f_const_addr[AW]) |
||(f_ctag_here != f_const_tag)); |
|
if ((state == DC_READC)&&(wb_start[AW-1:LS] == f_const_tag)) |
begin |
`ASSERT(f_nacks <= f_const_tag[LS-1:0]); |
if (f_nacks == f_const_tag[LS-1:0]) |
assume(!i_wb_ack); |
end |
end |
|
`endif // DCACHE |
|
//////////////////////////////////////////////// |
// |
// Checking the lock |
// |
//////////////////////////////////////////////// |
// |
// |
|
always @(*) |
`ASSERT((!lock_gbl)||(!lock_lcl)); |
always @(*) |
if (!OPT_LOCK) |
`ASSERT((!lock_gbl)&&(!lock_lcl)); |
|
//////////////////////////////////////////////// |
// |
// State based properties |
// |
//////////////////////////////////////////////// |
// |
// |
reg [F_LGDEPTH-1:0] f_rdpending; |
|
initial f_rdpending = 0; |
always @(posedge i_clk) |
if ((i_reset)||(o_err)) |
f_rdpending <= 0; |
else case({ (i_pipe_stb)&&(!i_op[0]), o_valid }) |
2'b01: f_rdpending <= f_rdpending - 1'b1; |
2'b10: f_rdpending <= f_rdpending + 1'b1; |
default: begin end |
endcase |
|
wire f_wb_cachable; |
iscachable #(.ADDRESS_WIDTH(AW)) |
f_chkwb_addr(o_wb_addr, f_wb_cachable); |
|
|
always @(*) |
if (state == DC_IDLE) |
begin |
`ASSERT(!r_wb_cyc_gbl); |
`ASSERT(!r_wb_cyc_lcl); |
|
`ASSERT(!cyc); |
|
if ((r_rd_pending)||(r_dvalid)||(r_svalid)) |
`ASSERT(o_busy); |
|
if (!OPT_PIPE) |
begin |
if (r_rd_pending) |
`ASSERT(o_busy); |
else if (r_svalid) |
`ASSERT(o_busy); |
else if (o_valid) |
`ASSERT(!o_busy); |
else if (o_err) |
`ASSERT(!o_busy); |
end |
end else begin |
`ASSERT(o_busy); |
`ASSERT(cyc); |
end |
|
|
|
always @(posedge i_clk) |
if (state == DC_IDLE) |
begin |
if (r_svalid) |
begin |
`ASSERT(!r_dvalid); |
`ASSERT(!r_rd_pending); |
if (!OPT_PIPE) |
`ASSERT(!o_valid); |
else if (o_valid) |
`ASSERT(f_rdpending == 2); |
end |
|
if (r_dvalid) |
begin |
`ASSERT(!r_rd_pending); |
`ASSERT(npending == 0); |
`ASSERT(f_rdpending == 1); |
end |
|
if (r_rd_pending) |
begin |
if ((OPT_PIPE)&&(o_valid)) |
`ASSERT(f_rdpending <= 2); |
else |
`ASSERT(f_rdpending == 1); |
|
end else if ((OPT_PIPE)&&(o_valid)&&($past(r_dvalid|r_svalid))) |
`ASSERT(f_rdpending <= 2); |
else |
`ASSERT(f_rdpending <= 1); |
end |
|
always @(posedge i_clk) |
if (state == DC_READC) |
begin |
`ASSERT( o_wb_cyc_gbl); |
`ASSERT(!o_wb_cyc_lcl); |
`ASSERT(!o_wb_we); |
`ASSERT(f_wb_cachable); |
|
`ASSERT(r_rd_pending); |
`ASSERT(r_cachable); |
if (($past(cyc))&&(!$past(o_wb_stb_gbl))) |
`ASSERT(!o_wb_stb_gbl); |
|
if ((OPT_PIPE)&&(o_valid)) |
`ASSERT(f_rdpending == 2); |
else |
`ASSERT(f_rdpending == 1); |
end |
|
always @(*) |
if (state == DC_READS) |
begin |
`ASSERT(!o_wb_we); |
|
if (OPT_PIPE) |
begin |
if (o_valid) |
`ASSERT((f_rdpending == npending + 1) |
||(f_rdpending == npending)); |
else |
`ASSERT(f_rdpending == npending); |
end |
end else if (state == DC_WRITE) |
`ASSERT(o_wb_we); |
|
always @(posedge i_clk) |
if ((state == DC_READS)||(state == DC_WRITE)) |
begin |
`ASSERT(o_wb_we == (state == DC_WRITE)); |
`ASSERT(!r_rd_pending); |
if (o_wb_we) |
`ASSERT(f_rdpending == 0); |
|
if (OPT_PIPE) |
begin |
casez({ $past(i_pipe_stb), f_stb }) |
2'b00: `ASSERT(npending == f_outstanding); |
2'b1?: `ASSERT(npending == f_outstanding + 1); |
2'b01: `ASSERT(npending == f_outstanding + 1); |
endcase |
|
if (state == DC_WRITE) |
`ASSERT(!o_valid); |
end else |
`ASSERT(f_outstanding <= 1); |
end |
|
always @(*) |
if (OPT_PIPE) |
`ASSERT(f_rdpending <= 2); |
else |
`ASSERT(f_rdpending <= 1); |
|
always @(posedge i_clk) |
if ((!OPT_PIPE)&&(o_valid)) |
`ASSERT(f_rdpending == 1); |
else if (o_valid) |
`ASSERT(f_rdpending >= 1); |
|
|
always @(*) |
if ((!o_busy)&&(!o_err)&&(!o_valid)) |
`ASSERT(f_rdpending == 0); |
|
always @(*) |
`ASSERT(cyc == ((r_wb_cyc_gbl)||(r_wb_cyc_lcl))); |
|
always @(*) |
if ((!i_reset)&&(f_nreqs == f_nacks)&&(!f_stb)) |
`ASSERT(!cyc); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_err))) |
`ASSUME(!i_lock); |
else if ((f_past_valid)&&(OPT_LOCK)&&($past(i_lock)) |
&&((!$past(o_valid)) || ($past(i_pipe_stb)))) |
`ASSUME($stable(i_lock)); |
|
|
//////////////////////////////////////////////// |
// |
// Ad-hoc properties |
// |
//////////////////////////////////////////////// |
// |
// |
always @(*) |
if ((OPT_PIPE)&&(state == DC_WRITE)&&(!i_wb_stall)&&(stb) |
&&(!npending[DP])) |
`ASSERT(!o_pipe_stalled); |
|
always @(posedge i_clk) |
if (state == DC_WRITE) |
`ASSERT(o_wb_we); |
else if ((state == DC_READS)||(state == DC_READC)) |
`ASSERT(!o_wb_we); |
|
always @(*) |
if (cyc) |
`ASSERT(f_cyc); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(cyc))&&(!c_wr)&&(last_tag_valid) |
&&(!r_rd_pending)) |
`ASSERT((c_v[last_tag[(CS-LS-1):0]]) |
&&(c_vtags[last_tag[(CS-LS-1):0]] == last_tag)); |
|
always @(*) |
if (!OPT_LOCAL_BUS) |
begin |
`ASSERT(r_wb_cyc_lcl == 1'b0); |
`ASSERT(o_wb_stb_lcl == 1'b0); |
`ASSERT(lock_lcl == 1'b0); |
end |
|
always @(posedge i_clk) |
if ((state == DC_READC)&&(!stb)) |
begin |
`ASSERT(o_wb_addr[LS-1:0] == 0); |
`ASSERT(o_wb_addr[AW-1:CS] == r_addr[AW-1:CS]); |
end else if ((state == DC_READC)&&(stb)) |
begin |
`ASSERT(o_wb_addr[AW-1:CS] == r_addr[AW-1:CS]); |
`ASSERT(o_wb_addr[LS-1:0] == f_nreqs[LS-1:0]); |
end |
|
wire [CS-1:0] f_expected_caddr; |
assign f_expected_caddr = { r_ctag[CS-LS-1:0], {(LS){1'b0}} }-1 |
+ f_nacks; |
always @(posedge i_clk) |
if (state == DC_READC) |
begin |
if (LS == 0) |
`ASSERT(end_of_line); |
else if (f_nacks < (1<<LS)-1) |
`ASSERT(!end_of_line); |
else if (f_nacks == (1<<LS)-1) |
`ASSERT(end_of_line); |
`ASSERT(f_nacks <= (1<<LS)); |
`ASSERT(f_nreqs <= (1<<LS)); |
if (f_nreqs < (1<<LS)) |
begin |
`ASSERT(o_wb_stb_gbl); |
`ASSERT(o_wb_addr[(LS-1):0] == f_nreqs[LS-1:0]); |
end else |
`ASSERT(!f_stb); |
`ASSERT((f_nreqs == 0)||(f_nacks <= f_nreqs)); |
`ASSERT(c_waddr == f_expected_caddr); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(r_rd)&&(!$past(i_reset))) |
begin |
`ASSERT((o_busy)||(r_svalid)); |
end |
|
always @(posedge i_clk) |
if (!$past(o_busy)) |
`ASSERT(!r_dvalid); |
|
always @(posedge i_clk) |
if ((state == DC_READC)&&(c_wr)) |
`ASSERT(c_wsel == 4'hf); |
|
always @(*) |
if (c_wr) |
`ASSERT((c_wsel == 4'hf) |
||(c_wsel == 4'hc) |
||(c_wsel == 4'h3) |
||(c_wsel == 4'h8) |
||(c_wsel == 4'h4) |
||(c_wsel == 4'h2) |
||(c_wsel == 4'h1)); |
|
always @(*) |
if (!OPT_PIPE) |
`ASSERT(o_pipe_stalled == o_busy); |
else if (o_pipe_stalled) |
`ASSERT(o_busy); |
|
// |
// Only ever abort on reset |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(cyc))&&(!$past(i_wb_err))) |
begin |
if (($past(i_pipe_stb))&&(!$past(o_pipe_stalled))) |
`ASSERT(cyc); |
else if ($past(f_outstanding > 1)) |
`ASSERT(cyc); |
else if (($past(f_outstanding == 1)) |
&&((!$past(i_wb_ack)) |
||(($past(f_stb)) |
&&(!$past(i_wb_stall))))) |
`ASSERT(cyc); |
else if (($past(f_outstanding == 0)) |
&&($past(f_stb)&&(!$past(i_wb_ack)))) |
`ASSERT(cyc); |
end |
|
always @(posedge i_clk) |
if ((OPT_PIPE)&&(f_past_valid)&&(!$past(i_reset))&&(state != DC_READC)) |
begin |
if ($past(cyc && i_wb_err)) |
begin |
`ASSERT(npending == 0); |
end else if (($past(i_pipe_stb))||($past(i_wb_stall && stb))) |
`ASSERT((npending == f_outstanding+1) |
||(npending == f_outstanding+2)); |
else |
`ASSERT(npending == f_outstanding); |
end |
|
always @(posedge i_clk) |
if ((OPT_PIPE)&&(state != DC_READC)&&(state != DC_IDLE)) |
`ASSERT(last_ack == (npending <= 1)); |
|
always @(*) |
`ASSERT(stb == f_stb); |
|
always @(*) |
if (r_rd_pending) |
`ASSERT(!r_svalid); |
|
always @(*) |
if (o_err) |
`ASSUME(!i_pipe_stb); |
|
always @(*) |
if (last_tag_valid) |
`ASSERT(|c_v); |
|
always @(posedge i_clk) |
if ((cyc)&&(state == DC_READC)&&($past(f_nacks > 0))) |
`ASSERT(!c_v[o_wb_addr[CS-1:LS]]); |
|
always @(*) |
if (last_tag_valid) |
begin |
`ASSERT((!cyc)||(o_wb_we)||(state == DC_READS) |
||(o_wb_addr[AW-1:LS] != last_tag)); |
end |
|
wire f_cachable_last_tag, f_cachable_r_addr; |
|
iscachable #(.ADDRESS_WIDTH(AW)) |
fccheck_last_tag({last_tag, {(LS){1'b0}}}, f_cachable_last_tag); |
|
iscachable #(.ADDRESS_WIDTH(AW)) |
fccheck_r_cachable(r_addr, f_cachable_r_addr); |
|
always @(*) |
if ((r_cachable)&&(r_rd_pending)) |
begin |
`ASSERT(state != DC_WRITE); |
// `ASSERT(state != DC_READS); |
`ASSERT(f_cachable_r_addr); |
if (cyc) |
`ASSERT(o_wb_addr[AW-1:LS] == r_addr[AW-1:LS]); |
end |
|
always @(*) |
if (last_tag_valid) |
begin |
`ASSERT(f_cachable_last_tag); |
`ASSERT(c_v[last_tag[CS-LS-1:0]]); |
`ASSERT(c_vtags[last_tag[CS-LS-1:0]]==last_tag); |
`ASSERT((state != DC_READC)||(last_tag != o_wb_addr[AW-1:LS])); |
end |
|
|
//////////////////////////////////////////////// |
// |
// Cover statements |
// |
//////////////////////////////////////////////// |
// |
// |
|
always @(posedge i_clk) |
cover(o_valid); |
|
always @(posedge i_clk) |
if (f_past_valid) |
cover($past(r_svalid)); |
|
generate if (OPT_PIPE) |
begin : PIPE_COVER |
|
wire recent_reset; |
reg [2:0] recent_reset_sreg; |
initial recent_reset_sreg = -1; |
always @(posedge i_clk) |
if (i_reset) |
recent_reset_sreg <= -1; |
else |
recent_reset_sreg <= { recent_reset_sreg[1:0], 1'b0 }; |
|
assign recent_reset = (i_reset)||(|recent_reset_sreg); |
|
// |
// |
wire f_cvr_cread = (!recent_reset)&&(i_pipe_stb)&&(!i_op[0]) |
&&(w_cachable); |
|
wire f_cvr_cwrite = (!recent_reset)&&(i_pipe_stb)&&(i_op[0]) |
&&(!cache_miss_inow); |
|
wire f_cvr_writes = (!recent_reset)&&(i_pipe_stb)&&(i_op[0]) |
&&(!w_cachable); |
wire f_cvr_reads = (!recent_reset)&&(i_pipe_stb)&&(!i_op[0]) |
&&(!w_cachable); |
wire f_cvr_test = (!recent_reset)&&(cyc); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_valid))) |
cover(o_valid); // ! |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_pipe_stb))) |
cover(i_pipe_stb); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_valid))&&($past(o_valid,2))) |
cover(o_valid); |
|
always @(posedge i_clk) |
cover(($past(f_cvr_cread))&&(f_cvr_cread)); |
|
always @(posedge i_clk) |
cover(($past(f_cvr_cwrite))&&(f_cvr_cwrite)); |
|
always @(posedge i_clk) |
cover(($past(f_cvr_writes))&&(f_cvr_writes)); |
|
/* |
* This cover statement will never pass. Why not? Because |
* cache reads must be separated from non-cache reads. Hence, |
* we can only allow a single non-cache read at a time, otherwise |
* we'd bypass the cache read logic. |
* |
always @(posedge i_clk) |
cover(($past(f_cvr_reads))&&(f_cvr_reads)); |
*/ |
|
// |
// This is unrealistic, as it depends upon the Wishbone |
// acknoledging the request on the same cycle |
always @(posedge i_clk) |
cover(($past(f_cvr_reads,2))&&(f_cvr_reads)); |
|
always @(posedge i_clk) |
cover(($past(r_dvalid))&&(r_svalid)); |
|
// |
// A minimum of one clock must separate two dvalid's. |
// This option is rather difficult to cover, since it means |
// we must first load two separate cache lines before |
// this can even be tried. |
always @(posedge i_clk) |
cover(($past(r_dvalid,2))&&(r_dvalid)); |
|
// |
// This is the optimal configuration we want: |
// i_pipe_stb |
// ##1 i_pipe_stb && r_svalid |
// ##1 r_svalid && o_valid |
// ##1 o_valid |
// It proves that we can handle a 2 clock delay, but that |
// we can also pipelin these cache accesses, so this |
// 2-clock delay becomes a 1-clock delay between pipelined |
// memory reads. |
// |
always @(posedge i_clk) |
cover(($past(r_svalid))&&(r_svalid)); |
|
// |
// While we'd never do this (it breaks the ZipCPU's pipeline |
// rules), it's nice to know we could. |
// i_pipe_stb && (!i_op[0]) // a read |
// ##1 i_pipe_stb && (i_op[0]) && r_svalid // a write |
// ##1 o_valid |
always @(posedge i_clk) |
cover(($past(r_svalid))&&(f_cvr_writes)); |
|
/* Unreachable |
* |
always @(posedge i_clk) |
cover(($past(f_cvr_writes))&&(o_valid)); |
|
always @(posedge i_clk) |
cover(($past(f_cvr_writes,2))&&(o_valid)); |
|
always @(posedge i_clk) |
cover(($past(f_cvr_writes,3))&&(o_valid)); |
|
always @(posedge i_clk) |
cover(($past(r_dvalid,3))&&(r_dvalid)); |
|
*/ |
|
always @(posedge i_clk) |
cover(($past(f_cvr_writes,4))&&(o_valid)); |
|
end endgenerate |
|
//////////////////////////////////////////////// |
// |
// Carelesss assumption section |
// |
//////////////////////////////////////////////// |
// |
// |
|
// |
// Can't jump from local to global mid lock |
always @(*) |
if((OPT_LOCK)&&(OPT_LOCAL_BUS)) |
begin |
if ((i_lock)&&(o_wb_cyc_gbl)&&(i_pipe_stb)) |
assume(!(&i_addr[(DW-1):(DW-8)])); |
else if ((i_lock)&&(o_wb_cyc_lcl)&&(i_pipe_stb)) |
assume(&i_addr[(DW-1):(DW-8)]); |
end |
|
always @(*) |
if ((OPT_PIPE)&&(o_busy || i_lock)&&(!o_pipe_stalled)) |
begin |
if (i_pipe_stb) |
assume((!OPT_LOCAL_BUS) |
||(f_pending_addr[AW]==(&i_addr[DW-1:DW-8]))); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(cyc))&&(!cyc)) |
assume((!i_wb_err)&&(!i_wb_ack)); |
|
`endif |
assign o_wb_cyc_gbl = (r_wb_cyc_gbl)||(lock_gbl); |
assign o_wb_cyc_lcl = (r_wb_cyc_lcl)||(lock_lcl); |
endmodule |
/core/pfcache.v
5,37 → 5,15
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// Purpose: Keeping our CPU fed with instructions, at one per clock and |
// with only a minimum number stalls. The entire cache may also |
// be cleared (if necessary). |
// with no stalls. An unusual feature of this cache is the |
// requirement that the entire cache may be cleared (if necessary). |
// |
// This logic is driven by a couple realities: |
// 1. It takes a clock to read from a block RAM address, and hence a clock |
// to read from the cache. |
// 2. It takes another clock to check that the tag matches |
// |
// Our goal will be to avoid this second check if at all possible. |
// Hence, we'll test on the clock of any given request whether |
// or not the request matches the last tag value, and on the next |
// clock whether it new tag value (if it has changed). Hence, |
// for anything found within the cache, there will be a one |
// cycle delay on any branch. |
// |
// |
// Address Words are separated into three components: |
// [ Tag bits ] [ Cache line number ] [ Cache position w/in the line ] |
// |
// On any read from the cache, only the second two components are required. |
// On any read from memory, the first two components will be fixed across |
// the bus, and the third component will be adjusted from zero to its |
// maximum value. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2019, Gisselquist Technology, LLC |
// Copyright (C) 2015-2017, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
59,62 → 37,36
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
// |
module pfcache(i_clk, i_reset, i_new_pc, i_clear_cache, |
module pfcache(i_clk, i_rst, i_new_pc, i_clear_cache, |
// i_early_branch, i_from_addr, |
i_stall_n, i_pc, o_insn, o_pc, o_valid, |
i_stall_n, i_pc, o_i, o_pc, o_v, |
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, |
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data, |
o_illegal |
`ifdef NOT_YET_READY |
, i_mmu_ack, i_mmu_we, i_mmu_paddr |
`endif |
`ifdef FORMAL |
, f_pc_wb |
`endif |
); |
`ifdef FORMAL |
parameter LGCACHELEN = 4, ADDRESS_WIDTH=30, |
LGLINES=2; // Log of the number of separate cache lines |
`else |
parameter LGCACHELEN = 12, ADDRESS_WIDTH=30, |
LGLINES=6; // Log of the number of separate cache lines |
`endif |
localparam CACHELEN=(1<<LGCACHELEN); //Wrd Size of our cache memory |
o_illegal); |
parameter LGCACHELEN = 8, ADDRESS_WIDTH=24, |
LGLINES=5; // Log of the number of separate cache lines |
localparam CACHELEN=(1<<LGCACHELEN); // Size of our cache memory |
localparam CW=LGCACHELEN; // Short hand for LGCACHELEN |
localparam LS=LGCACHELEN-LGLINES; // Size of a cache line |
localparam PW=LGCACHELEN-LGLINES; // Size of a cache line |
localparam BUSW = 32; // Number of data lines on the bus |
localparam AW=ADDRESS_WIDTH; // Shorthand for ADDRESS_WIDTH |
input i_clk, i_rst, i_new_pc; |
input i_clear_cache; |
input i_stall_n; |
input [(AW-1):0] i_pc; |
output wire [(BUSW-1):0] o_i; |
output wire [(AW-1):0] o_pc; |
output wire o_v; |
// |
input wire i_clk, i_reset; |
// |
// The interface with the rest of the CPU |
input wire i_new_pc; |
input wire i_clear_cache; |
input wire i_stall_n; |
input wire [(AW+1):0] i_pc; |
output wire [(BUSW-1):0] o_insn; |
output wire [(AW+1):0] o_pc; |
output wire o_valid; |
// |
// The wishbone bus interface |
output reg o_wb_cyc, o_wb_stb; |
output wire o_wb_we; |
output reg o_wb_cyc, o_wb_stb; |
output wire o_wb_we; |
output reg [(AW-1):0] o_wb_addr; |
output wire [(BUSW-1):0] o_wb_data; |
// |
input wire i_wb_ack, i_wb_stall, i_wb_err; |
input wire [(BUSW-1):0] i_wb_data; |
input i_wb_ack, i_wb_stall, i_wb_err; |
input [(BUSW-1):0] i_wb_data; |
// |
// o_illegal will be true if this instruction was the result of a |
// bus error (This is also part of the CPU interface) |
output reg o_illegal; |
// |
`ifdef NOT_YET_READY |
input wire i_mmu_ack, i_mmu_we; |
input wire [(PAW-1):0] i_mmu_paddr; |
`endif |
|
// Fixed bus outputs: we read from the bus only, never write. |
// Thus the output data is ... irrelevant and don't care. We set it |
122,132 → 74,64
assign o_wb_we = 1'b0; |
assign o_wb_data = 0; |
|
`ifdef NOT_YET_READY |
// These wires will be used below as part of the cache invalidation |
// routine, should the MMU be used. This allows us to snoop on the |
// physical side of the MMU bus, and invalidate any results should |
// we need to do so. |
wire mmu_inval; |
wire [(PAW-CW-1):0] mmu_mskaddr; |
`endif |
`ifdef FORMAL |
output wire [AW-1:0] f_pc_wb; |
assign f_pc_wb = i_pc[AW+1:2]; |
`endif |
|
|
wire r_v; |
reg [(BUSW-1):0] cache [0:((1<<CW)-1)]; |
reg [(AW-CW-1):0] cache_tags [0:((1<<(LGLINES))-1)]; |
reg [((1<<(LGLINES))-1):0] valid_mask; |
reg [(AW-CW-1):0] tags [0:((1<<(LGLINES))-1)]; |
reg [((1<<(LGLINES))-1):0] vmask; |
|
reg r_v_from_pc, r_v_from_last, r_new_request; |
reg rvsrc; |
wire w_v_from_pc, w_v_from_last; |
reg [(AW+1):0] lastpc; |
reg [(CW-1):0] wraddr; |
reg [(AW-1):0] lastpc; |
reg [(CW-1):0] rdaddr; |
reg [(AW-1):CW] tagvalipc, tagvallst; |
wire [(AW-1):CW] tagval; |
wire [(AW-1):LS] lasttag; |
wire [(AW-1):PW] lasttag; |
reg illegal_valid; |
reg [(AW-1):LS] illegal_cache; |
reg [(AW-1):PW] illegal_cache; |
|
// initial o_i = 32'h76_00_00_00; // A NOOP instruction |
// initial o_pc = 0; |
reg [(BUSW-1):0] r_pc_cache, r_last_cache; |
reg [(AW+1):0] r_pc, r_lastpc; |
reg isrc; |
reg [1:0] delay; |
reg svmask, last_ack, needload, last_addr, |
bus_abort; |
reg [(LGLINES-1):0] saddr; |
|
wire w_advance; |
assign w_advance = (i_new_pc)||((r_v)&&(i_stall_n)); |
|
///////////////////////////////////////////////// |
// |
// Read the instruction from the cache |
// |
///////////////////////////////////////////////// |
// |
// |
// We'll read two values from the cache, the first is the value if |
// i_pc contains the address we want, the second is the value we'd read |
// if lastpc (i.e. $past(i_pc)) was the address we wanted. |
initial r_pc = 0; |
initial r_lastpc = 0; |
reg [(AW-1):0] r_pc, r_lastpc; |
reg isrc; |
always @(posedge i_clk) |
begin |
// We don't have the logic to select what to read, we must |
// read both the value at i_pc and lastpc. cache[i_pc] is |
// the value we return if the last cache request was in the |
// cache on the last clock, cacne[lastpc] is the value we |
// return if we've been stalled, weren't valid, or had to wait |
// a clock or two. |
// the value we return if the cache is good, cacne[lastpc] is |
// the value we return if we've been stalled, weren't valid, |
// or had to wait a clock or two. (Remember i_pc can't stop |
// changing for a clock, so we need to keep track of the last |
// one from before it stopped.) |
// |
// Part of the issue here is that i_pc is going to increment |
// on this clock before we know whether or not the cache entry |
// we've just read is valid. We can't stop this. Hence, we |
// need to read from the lastpc entry. |
// |
// |
// Here we keep track of which answer we want/need. |
// If we reported a valid value to the CPU on the last clock, |
// and the CPU wasn't stalled, then we want to use i_pc. |
// Likewise if the CPU gave us an i_new_pc request, then we'll |
// want to return the value associated with reading the cache |
// at i_pc. |
isrc <= w_advance; |
// Here we keep track of which answer we want/need |
isrc <= ((r_v)&&(i_stall_n))||(i_new_pc); |
|
// Here we read both cache entries, at i_pc and lastpc. |
// We'll select from among these cache possibilities on the |
// next clock |
r_pc_cache <= cache[i_pc[(CW+1):2]]; |
r_last_cache <= cache[lastpc[(CW+1):2]]; |
// |
// Let's also register(delay) the r_pc and r_lastpc values |
// for the next clock, so we can accurately report the address |
// of the cache value we just looked up. |
// Here we read both, and select which was write using isrc |
// on the next clock. |
r_pc_cache <= cache[i_pc[(CW-1):0]]; |
r_last_cache <= cache[lastpc[(CW-1):0]]; |
r_pc <= i_pc; |
r_lastpc <= lastpc; |
end |
assign o_pc = (isrc) ? r_pc : r_lastpc; |
assign o_i = (isrc) ? r_pc_cache : r_last_cache; |
|
// On our next clock, our result with either be the registered i_pc |
// value from the last clock (if isrc), otherwise r_lastpc |
assign o_pc = (isrc) ? r_pc : r_lastpc; |
// The same applies for determining what the next output instruction |
// will be. We just read it in the last clock, now we just need to |
// select between the two possibilities we just read. |
assign o_insn= (isrc) ? r_pc_cache : r_last_cache; |
|
|
///////////////////////////////////////////////// |
// |
// Read the tag value associated with this tcache line |
// |
///////////////////////////////////////////////// |
// |
// |
|
// |
// Read the tag value associated with this i_pc value |
reg tagsrc; |
always @(posedge i_clk) |
// It may be possible to recover a clock once the cache line |
// has been filled, but our prior attempt to do so has lead |
// to a race condition, so we keep this logic simple. |
if (((r_v)&&(i_stall_n))||(i_clear_cache)||(i_new_pc)) |
tagsrc <= 1'b1; |
else |
tagsrc <= 1'b0; |
initial tagvalipc = 0; |
always @(posedge i_clk) |
tagvalipc <= cache_tags[i_pc[(CW+1):LS+2]]; |
|
|
// |
// Read the tag value associated with the lastpc value, from what |
// i_pc was when we could not tell if this value was in our cache or |
// not, or perhaps from when we determined that i was not in the cache. |
tagvalipc <= tags[i_pc[(CW-1):PW]]; |
initial tagvallst = 0; |
always @(posedge i_clk) |
tagvallst <= cache_tags[lastpc[(CW+1):LS+2]]; |
tagvallst <= tags[lastpc[(CW-1):PW]]; |
assign tagval = (tagsrc)?tagvalipc : tagvallst; |
|
// Select from between these two values on the next clock |
assign tagval = (isrc)?tagvalipc : tagvallst; |
|
// i_pc will only increment when everything else isn't stalled, thus |
// we can set it without worrying about that. Doing this enables |
// us to work in spite of stalls. For example, if the next address |
255,724 → 139,170
// anyway. |
initial lastpc = 0; |
always @(posedge i_clk) |
if (w_advance) |
lastpc <= i_pc; |
if (((r_v)&&(i_stall_n))||(i_clear_cache)||(i_new_pc)) |
lastpc <= i_pc; |
|
assign lasttag = lastpc[(AW+1):LS+2]; |
assign lasttag = lastpc[(AW-1):PW]; |
|
///////////////////////////////////////////////// |
// |
// Use the tag value to determine if our output instruction will be |
// valid. |
// |
///////////////////////////////////////////////// |
// |
// |
assign w_v_from_pc = ((i_pc[(AW+1):LS+2] == lasttag) |
&&(tagval == i_pc[(AW+1):CW+2]) |
&&(valid_mask[i_pc[(CW+1):LS+2]])); |
assign w_v_from_last = ((tagval == lastpc[(AW+1):CW+2]) |
&&(valid_mask[lastpc[(CW+1):LS+2]])); |
wire w_v_from_pc, w_v_from_last; |
assign w_v_from_pc = ((i_pc[(AW-1):PW] == lasttag) |
&&(tagvalipc == i_pc[(AW-1):CW]) |
&&(vmask[i_pc[(CW-1):PW]])); |
assign w_v_from_last = ( |
//(lastpc[(AW-1):PW] == lasttag)&& |
(tagval == lastpc[(AW-1):CW]) |
&&(vmask[lastpc[(CW-1):PW]])); |
|
reg [1:0] delay; |
|
initial delay = 2'h3; |
reg rvsrc; |
always @(posedge i_clk) |
if ((i_reset)||(i_clear_cache)||(w_advance)) |
begin |
// Source our valid signal from i_pc |
rvsrc <= 1'b1; |
// Delay at least two clocks before declaring that |
// we have an invalid result. This will give us time |
// to check the tag value of what's in the cache. |
delay <= 2'h2; |
end else if ((!r_v)&&(!o_illegal)) begin |
// If we aren't sourcing our valid signal from the |
// i_pc clock, then we are sourcing it from the |
// lastpc clock (one clock later). If r_v still |
// isn't valid, we may need to make a bus request. |
// Apply our timer and timeout. |
rvsrc <= 1'b0; |
|
// Delay is two once the bus starts, in case the |
// bus transaction needs to be restarted upon completion |
// This might happen if, after we start loading the |
// cache, we discover a branch. The cache load will |
// still complete, but the branches address needs to be |
// the onen we jump to. This may mean we need to load |
// the cache twice. |
if (o_wb_cyc) |
if ((i_rst)||(i_clear_cache)||(i_new_pc)||((r_v)&&(i_stall_n))) |
begin |
// r_v <= r_v_from_pc; |
rvsrc <= 1'b1; |
delay <= 2'h2; |
else if (delay != 0) |
delay <= delay + 2'b11; // i.e. delay -= 1; |
end else begin |
// After sourcing our output from i_pc, if it wasn't |
// accepted, source the instruction from the lastpc valid |
// determination instead |
rvsrc <= 1'b0; |
if (o_illegal) |
delay <= 2'h2; |
end |
end else if (~r_v) begin // Otherwise, r_v was true and we were |
// stalled, hence only if ~r_v |
rvsrc <= 1'b0; |
if (o_wb_cyc) |
delay <= 2'h2; |
else if (delay != 0) |
delay <= delay + 2'b11; // i.e. delay -= 1; |
end |
reg r_v_from_pc, r_v_from_last; |
always @(posedge i_clk) |
r_v_from_pc <= w_v_from_pc; |
always @(posedge i_clk) |
r_v_from_last <= w_v_from_last; |
|
wire w_invalidate_result; |
assign w_invalidate_result = (i_reset)||(i_clear_cache); |
assign r_v = ((rvsrc)?(r_v_from_pc):(r_v_from_last)); |
assign o_v = (((rvsrc)?(r_v_from_pc):(r_v_from_last)) |
||((o_illegal)&&(~o_wb_cyc))) |
&&(~i_new_pc)&&(~i_rst); |
|
reg r_prior_illegal; |
initial r_prior_illegal = 0; |
initial r_new_request = 0; |
initial r_v_from_pc = 0; |
initial r_v_from_last = 0; |
reg last_ack; |
initial last_ack = 1'b0; |
always @(posedge i_clk) |
begin |
r_new_request <= w_invalidate_result; |
r_v_from_pc <= (w_v_from_pc)&&(!w_invalidate_result) |
&&(!o_illegal); |
r_v_from_last <= (w_v_from_last)&&(!w_invalidate_result); |
last_ack <= (o_wb_cyc)&&( |
(rdaddr[(PW-1):1]=={(PW-1){1'b1}}) |
&&((rdaddr[0])||(i_wb_ack))); |
|
r_prior_illegal <= (o_wb_cyc)&&(i_wb_err); |
end |
|
// Now use rvsrc to determine which of the two valid flags we'll be |
// using: r_v_from_pc (the i_pc address), or r_v_from_last (the lastpc |
// address) |
assign r_v = ((rvsrc)?(r_v_from_pc):(r_v_from_last))&&(!r_new_request); |
assign o_valid = (((rvsrc)?(r_v_from_pc):(r_v_from_last)) |
||(o_illegal)) |
&&(!i_new_pc)&&(!r_prior_illegal); |
|
///////////////////////////////////////////////// |
// |
// If the instruction isn't in our cache, then we need to load |
// a new cache line from memory. |
// |
///////////////////////////////////////////////// |
// |
// |
reg needload; |
initial needload = 1'b0; |
always @(posedge i_clk) |
if ((i_clear_cache)||(o_wb_cyc)) |
needload <= 1'b0; |
else if ((w_advance)&&(!o_illegal)) |
needload <= 1'b0; |
else |
needload <= (delay==0)&&(!w_v_from_last) |
// Prevent us from reloading an illegal address |
// (i.e. one that produced a bus error) over and over |
// and over again |
&&((!illegal_valid) |
||(lastpc[(AW+1):LS+2] != illegal_cache)); |
needload <= ((~r_v)&&(delay==0) |
&&((tagvallst != lastpc[(AW-1):CW]) |
||(~vmask[lastpc[(CW-1):PW]])) |
&&((~illegal_valid) |
||(lastpc[(AW-1):PW] != illegal_cache))); |
|
// |
// Working from the rule that you want to keep complex logic out of |
// a state machine if possible, we calculate a "last_stb" value one |
// clock ahead of time. Hence, any time a request is accepted, if |
// last_stb is also true we'll know we need to drop the strobe line, |
// having finished requesting a complete cache line. |
reg last_addr; |
initial last_addr = 1'b0; |
always @(posedge i_clk) |
if (!o_wb_cyc) |
last_addr <= 1'b0; |
else if ((o_wb_addr[(LS-1):1] == {(LS-1){1'b1}}) |
&&((!i_wb_stall)|(o_wb_addr[0]))) |
last_addr <= 1'b1; |
last_addr <= (o_wb_cyc)&&(o_wb_addr[(PW-1):1] == {(PW-1){1'b1}}) |
&&((~i_wb_stall)|(o_wb_addr[0])); |
|
// |
// "last_ack" is almost identical to last_addr, save that this |
// will be true on the same clock as the last acknowledgment from the |
// bus. The state machine logic will use this to determine when to |
// get off the bus and end the wishbone bus cycle. |
initial last_ack = 1'b0; |
always @(posedge i_clk) |
last_ack <= (o_wb_cyc)&&( |
(wraddr[(LS-1):1]=={(LS-1){1'b1}}) |
&&((wraddr[0])||(i_wb_ack))); |
|
initial bus_abort = 1'b0; |
always @(posedge i_clk) |
if (!o_wb_cyc) |
bus_abort <= 1'b0; |
else if ((i_clear_cache)||(i_new_pc)) |
bus_abort <= 1'b1; |
|
// |
// Here's the difficult piece of state machine logic--the part that |
// determines o_wb_cyc and o_wb_stb. We've already moved most of the |
// complicated logic off of this statemachine, calculating it one cycle |
// early. As a result, this is a fairly easy piece of logic. |
initial o_wb_cyc = 1'b0; |
initial o_wb_stb = 1'b0; |
initial o_wb_addr = {(AW){1'b0}}; |
initial rdaddr = 0; |
always @(posedge i_clk) |
if ((i_reset)||(i_clear_cache)) |
begin |
o_wb_cyc <= 1'b0; |
o_wb_stb <= 1'b0; |
end else if (o_wb_cyc) |
begin |
if (i_wb_err) |
if ((i_rst)||(i_clear_cache)) |
begin |
o_wb_cyc <= 1'b0; |
o_wb_stb <= 1'b0; |
else if ((o_wb_stb)&&(!i_wb_stall)&&(last_addr)) |
o_wb_stb <= 1'b0; |
end else if (o_wb_cyc) |
begin |
if (i_wb_err) |
o_wb_stb <= 1'b0; |
else if ((o_wb_stb)&&(~i_wb_stall)&&(last_addr)) |
o_wb_stb <= 1'b0; |
|
if (((i_wb_ack)&&(last_ack))||(i_wb_err)) |
o_wb_cyc <= 1'b0; |
if (((i_wb_ack)&&(last_ack))||(i_wb_err)) |
o_wb_cyc <= 1'b0; |
|
end else if ((needload)&&(!i_new_pc)) |
begin |
o_wb_cyc <= 1'b1; |
o_wb_stb <= 1'b1; |
end |
// else if (rdaddr[(PW-1):1] == {(PW-1){1'b1}}) |
// tags[lastpc[(CW-1):PW]] <= lastpc[(AW-1):CW]; |
|
// If we are reading from this cache line, then once we get the first |
// acknowledgement, this cache line has the new tag value |
always @(posedge i_clk) |
if ((o_wb_cyc)&&(i_wb_ack)) |
cache_tags[o_wb_addr[(CW-1):LS]] <= o_wb_addr[(AW-1):CW]; |
end else if (needload) |
begin |
o_wb_cyc <= 1'b1; |
o_wb_stb <= 1'b1; |
end |
|
|
// On each acknowledgment, increment the address we use to write into |
// our cache. Hence, this is the write address into our cache block |
// RAM. |
initial wraddr = 0; |
always @(posedge i_clk) |
if ((o_wb_cyc)&&(i_wb_ack)&&(!last_ack)) |
wraddr <= wraddr + 1'b1; |
else if (!o_wb_cyc) |
wraddr <= { lastpc[(CW+1):LS+2], {(LS){1'b0}} }; |
|
// |
// The wishbone request address. This has meaning anytime o_wb_stb |
// is active, and needs to be incremented any time an address is |
// accepted--WITH THE EXCEPTION OF THE LAST ADDRESS. We need to keep |
// this steady for that last address, unless the last address returns |
// a bus error. In that case, the whole cache line will be marked as |
// invalid--but we'll need the value of this register to know how |
// to do that propertly. |
initial o_wb_addr = {(AW){1'b0}}; |
if (o_wb_cyc) // &&(i_wb_ack) |
tags[o_wb_addr[(CW-1):PW]] <= o_wb_addr[(AW-1):CW]; |
always @(posedge i_clk) |
if ((o_wb_stb)&&(!i_wb_stall)&&(!last_addr)) |
o_wb_addr[(LS-1):0] <= o_wb_addr[(LS-1):0]+1'b1; |
else if (!o_wb_cyc) |
o_wb_addr <= { lastpc[(AW+1):LS+2], {(LS){1'b0}} }; |
if ((o_wb_cyc)&&(i_wb_ack)) |
rdaddr <= rdaddr + 1; |
else if (~o_wb_cyc) |
rdaddr <= { lastpc[(CW-1):PW], {(PW){1'b0}} }; |
|
always @(posedge i_clk) |
if ((o_wb_stb)&&(~i_wb_stall)&&(~last_addr)) |
o_wb_addr[(PW-1):0] <= o_wb_addr[(PW-1):0]+1; |
else if (~o_wb_cyc) |
o_wb_addr <= { lastpc[(AW-1):PW], {(PW){1'b0}} }; |
|
// Since it is impossible to initialize an array, our cache will start |
// up cache uninitialized. We'll also never get a valid ack without |
// cyc being active, although we might get one on the clock after |
// cyc was active--so we need to test and gate on whether o_wb_cyc |
// is true. |
// |
// wraddr will advance forward on every clock cycle where ack is true, |
// hence we don't need to check i_wb_ack here. This will work because |
// multiple writes to the same address, ending with a valid write, |
// will always yield the valid write's value only after our bus cycle |
// is over. |
// Can't initialize an array, so leave cache uninitialized |
// We'll also never get an ack without sys being active, so skip |
// that check. Or rather, let's just use o_wb_cyc instead. This |
// will work because multiple writes to the same address, ending with |
// a valid write, aren't a problem. |
always @(posedge i_clk) |
if (o_wb_cyc) |
cache[wraddr] <= i_wb_data; |
if (o_wb_cyc) // &&(i_wb_ack) |
cache[rdaddr] <= i_wb_data; |
|
// VMask ... is a section loaded? |
// Note "svmask". It's purpose is to delay the valid_mask setting by |
// one clock, so that we can insure the right value of the cache is |
// loaded before declaring that the cache line is valid. Without |
// this, the cache line would get read, and the instruction would |
// read from the last cache line. |
initial valid_mask = 0; |
// Note "svmask". It's purpose is to delay the vmask setting by one |
// clock, so that we can insure the right value of the cache is loaded |
// before declaring that the cache line is valid. Without this, the |
// cache line would get read, and the instruction would read from the |
// last cache line. |
reg svmask; |
initial vmask = 0; |
initial svmask = 1'b0; |
reg [(LGLINES-1):0] saddr; |
always @(posedge i_clk) |
if ((i_reset)||(i_clear_cache)) |
begin |
valid_mask <= 0; |
svmask<= 1'b0; |
end else begin |
svmask <= ((o_wb_cyc)&&(i_wb_ack)&&(last_ack)&&(!bus_abort)); |
|
if (svmask) |
valid_mask[saddr] <= (!bus_abort); |
if ((!o_wb_cyc)&&(needload)) |
valid_mask[lastpc[(CW+1):LS+2]] <= 1'b0; |
`ifdef NOT_YET_READY |
// |
// MMU code |
// |
if (mmu_inval) |
valid_mask[mmu_mskadr] <= 1'b0; |
`endif |
end |
|
always @(posedge i_clk) |
if ((o_wb_cyc)&&(i_wb_ack)) |
saddr <= wraddr[(CW-1):LS]; |
// MMU code |
// |
// |
`ifdef NOT_YET_READY |
parameter [0:0] USE_MMU = 1'b1; |
generate if (USE_MMU) |
begin |
reg [(PAW-CW-1):0] ptag [0:((1<<(LGLINES))-1)]; |
reg mmu_pre_inval, r_mmu_inval; |
reg [(PAW-CW-1):0] mmu_pre_tag, mmu_pre_padr; |
reg [(CW-LS-1):0] r_mmu_mskadr; |
|
initial mmu_pre_inval = 0; |
initial mmu_pre_tag = 0; |
initial mmu_pre_padr = 0; |
initial mmu_pre2_inval = 0; |
initial mmu_pre2_mskadr = 0; |
|
always @(posedge i_clk) |
if ((o_wb_cyc)&&(!last_addr)&&(i_mmu_ack)) |
ptag[i_mmu_paddr[(CW-1):LS]] <= i_mmu_paddr[(PAW-1):CW]; |
|
always @(posedge i_clk) |
if (i_reset) |
if ((i_rst)||(i_clear_cache)) |
begin |
mmu_pre_inval <= 0; |
r_mmu_inval <= 0; |
end else begin |
mmu_pre_inval <= (i_mmu_ack)&&(i_mmu_we); |
r_mmu_inval <= (mmu_pre_inval)&&(mmu_pre_inval) |
&&(mmu_pre_tag == mmu_pre_paddr); |
vmask <= 0; |
svmask<= 1'b0; |
end |
|
always @(posedge i_clk) |
mmu_pre_tag <= ptag[i_mmu_paddr[(CW-1):LS]]; |
|
always @(posedge i_clk) |
begin |
mmu_pre_padr <= i_mmu_paddr[(PAW-1):CW]; |
|
r_mmu_mskadr <= mmu_pre_padr[(PAW-LS-1):(CW-LS)]; |
else begin |
svmask <= ((o_wb_cyc)&&(i_wb_ack)&&(last_ack)); |
|
if (svmask) |
vmask[saddr] <= 1'b1; |
if ((~o_wb_cyc)&&(needload)) |
vmask[lastpc[(CW-1):PW]] <= 1'b0; |
end |
always @(posedge i_clk) |
if ((o_wb_cyc)&&(i_wb_ack)) |
saddr <= rdaddr[(CW-1):PW]; |
|
assign mmu_inval = r_mmu_inval; |
assign mmu_mskadr = r_mmu_mskadr; |
end else begin |
assign mmu_inval = 0; |
assign mmu_mskadr = 0; |
end endgenerate |
`endif |
|
///////////////////////////////////////////////// |
// |
// Handle bus errors here. If a bus read request |
// returns an error, then we'll mark the entire |
// line as having a (valid) illegal value. |
// |
///////////////////////////////////////////////// |
// |
// |
// |
// |
initial illegal_cache = 0; |
initial illegal_valid = 0; |
always @(posedge i_clk) |
if ((i_reset)||(i_clear_cache)) |
begin |
illegal_cache <= 0; |
illegal_valid <= 0; |
end else if ((o_wb_cyc)&&(i_wb_err)) |
begin |
illegal_cache <= o_wb_addr[(AW-1):LS]; |
illegal_valid <= 1'b1; |
end |
|
initial o_illegal = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(i_clear_cache)||(i_new_pc)) |
o_illegal <= 1'b0; |
else if ((o_illegal)||((o_valid)&&(i_stall_n))) |
o_illegal <= 1'b0; |
else |
o_illegal <= (illegal_valid) |
&&(illegal_cache == lastpc[(AW+1):LS+2]); |
|
`ifdef FORMAL |
// |
// |
// Generic setup |
// |
// |
`ifdef PFCACHE |
`define ASSUME assume |
`else |
`define ASSUME assert |
`define STEP_CLOCK |
`endif |
|
// Keep track of a flag telling us whether or not $past() |
// will return valid results |
reg f_past_valid; |
initial f_past_valid = 1'b0; |
always @(posedge i_clk) |
f_past_valid = 1'b1; |
always @(*) |
if (!f_past_valid) |
`ASSUME(i_reset); |
|
///////////////////////////////////////////////// |
// |
// |
// Assumptions about our inputs |
// |
// |
///////////////////////////////////////////////// |
|
|
`ifdef PFCACHE |
// |
// Assume that resets, new-pc commands, and clear-cache commands |
// are never more than pulses--one clock wide at most. |
// |
// It may be that the CPU treats us differently. We'll only assume |
// our solver to this here. |
always @(posedge i_clk) |
if (!f_past_valid) |
begin |
if ($past(i_reset)) |
assume(!i_reset); |
if ($past(i_new_pc)) |
assume(!i_new_pc); |
if ($past(i_clear_cache)) |
assume(!i_clear_cache); |
end |
`endif |
|
// |
// Assume we start from a reset condition |
initial `ASSUME(i_reset); |
|
// Assume that any reset is either accompanied by a new address, |
// or a new address immediately follows it. |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_reset))) |
`ASSUME(i_new_pc); |
// |
// Let's make some assumptions about how long it takes our |
// phantom bus and phantom CPU to respond. |
// |
// These delays need to be long enough to flush out any potential |
// errors, yet still short enough that the formal method doesn't |
// take forever to solve. |
// |
localparam F_CPU_DELAY = 4; |
reg [4:0] f_cpu_delay; |
|
// Now, let's repeat this bit but now looking at the delay the CPU |
// takes to accept an instruction. |
always @(posedge i_clk) |
// If no instruction is ready, then keep our counter at zero |
if ((!o_valid)||(i_stall_n)) |
f_cpu_delay <= 0; |
else |
// Otherwise, count the clocks the CPU takes to respond |
f_cpu_delay <= f_cpu_delay + 1'b1; |
|
`ifdef PFCACHE |
always @(posedge i_clk) |
assume(f_cpu_delay < F_CPU_DELAY); |
`endif |
|
always @(posedge i_clk) |
if ($past(i_reset || i_clear_cache)) |
assume(i_stall_n); |
else if ($past(i_stall_n && !o_valid)) |
assume(i_stall_n); |
else if (i_new_pc) |
assume(i_stall_n); |
|
///////////////////////////////////////////////// |
// |
// |
// Assertions about our outputs |
// |
// |
///////////////////////////////////////////////// |
|
localparam F_LGDEPTH=LS+1; |
wire [(F_LGDEPTH-1):0] f_nreqs, f_nacks, f_outstanding; |
|
fwb_master #(.AW(AW), .DW(BUSW), .F_LGDEPTH(F_LGDEPTH), |
.F_MAX_STALL(2), .F_MAX_ACK_DELAY(3), |
.F_MAX_REQUESTS(1<<LS), .F_OPT_SOURCE(1), |
.F_OPT_RMW_BUS_OPTION(0), |
.F_OPT_DISCONTINUOUS(0)) |
f_wbm(i_clk, i_reset, |
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, 4'h0, |
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err, |
f_nreqs, f_nacks, f_outstanding); |
|
// writes are also illegal for a prefetch. |
always @(posedge i_clk) |
if (o_wb_stb) |
assert(!o_wb_we); |
|
always @(posedge i_clk) |
begin |
assert(f_nreqs <= (1<<LS)); |
if ((o_wb_cyc)&&(o_wb_stb)) |
assert(f_nreqs == o_wb_addr[(LS-1):0]); |
if ((f_past_valid)&&($past(o_wb_cyc)) |
&&(!o_wb_stb)&&(!$past(i_wb_err || i_reset || i_clear_cache))) |
assert(f_nreqs == (1<<LS)); |
end |
|
always @(posedge i_clk) |
if (f_past_valid) |
begin |
if ((!o_wb_cyc)&&($past(o_wb_cyc))&&(!$past(i_reset)) |
&&(!$past(i_clear_cache)) &&(!$past(i_wb_err))) |
assert(f_nacks == (1<<LS)); |
else if (o_wb_cyc) |
assert(f_nacks[(LS-1):0] == wraddr[(LS-1):0]); |
end |
|
// The last-ack line |
always @(posedge i_clk) |
if (o_wb_cyc) |
assert(last_ack == (f_nacks == ((1<<LS)-1))); |
|
// The valid line for whats being read |
always @(posedge i_clk) |
if (o_wb_cyc) |
assert(!valid_mask[o_wb_addr[CW-1:LS]]); |
|
always @(posedge i_clk) |
if ((illegal_valid)&&(o_wb_cyc)) |
assert(o_wb_addr[AW-1:LS] != illegal_cache); |
|
reg [((1<<(LGLINES))-1):0] f_past_valid_mask; |
initial f_past_valid_mask = 0; |
always @(posedge i_clk) |
f_past_valid_mask = valid_mask; |
|
always @(posedge i_clk) |
if ((o_valid)&&($past(!o_valid || !o_illegal))) |
assert((!o_wb_cyc) |
||(o_wb_addr[AW-1:LS] != o_pc[AW+1:LS+2])); |
always @(posedge i_clk) |
if (illegal_valid) |
begin |
assert((!o_wb_cyc) |
||(o_wb_addr[AW-1:LS] != illegal_cache)); |
|
// The illegal cache line should never be valid within our |
// cache |
assert((!valid_mask[illegal_cache[CW-1:LS]]) |
||(cache_tags[illegal_cache[CW-1:LS]] |
!= illegal_cache[AW-1:CW])); |
end |
|
///////////////////////////////////////////////////// |
// |
// |
// Assertions about our return responses to the CPU |
// |
// |
///////////////////////////////////////////////////// |
|
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_wb_cyc))) |
assert(o_wb_addr[(AW-1):LS] == $past(o_wb_addr[(AW-1):LS])); |
|
// Consider it invalid to present the CPU with the same instruction |
// twice in a row. |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_valid))&&($past(i_stall_n))&&(o_valid)) |
assert(o_pc != $past(o_pc)); |
|
always @(posedge i_clk) |
if (o_valid) |
begin |
if (!o_illegal) |
if ((i_rst)||(i_clear_cache)) |
begin |
assert(cache_tags[o_pc[(CW+1):LS+2]] == o_pc[(AW+1):CW+2]); |
assert(valid_mask[o_pc[(CW+1):LS+2]] || (o_illegal)); |
assert(o_insn == cache[o_pc[(CW+1):2]]); |
assert((!illegal_valid) |
||((illegal_cache != o_pc[(AW+1):LS+2]))); |
illegal_cache <= 0; |
illegal_valid <= 0; |
end else if ((o_wb_cyc)&&(i_wb_err)) |
begin |
illegal_cache <= o_wb_addr[(AW-1):PW]; |
illegal_valid <= 1'b1; |
end |
|
assert(o_illegal == ($past(illegal_valid) |
&&($past(illegal_cache)== o_pc[(AW+1):LS+2]))); |
end |
|
always @(*) |
begin |
`ASSUME(i_pc[1:0] == 2'b00); |
assert(o_pc[1:0] == 2'b00); |
assert(r_pc[1:0] == 2'b00); |
assert(r_lastpc[1:0] == 2'b00); |
end |
|
reg [(AW+1):0] f_next_pc; |
|
initial o_illegal = 1'b0; |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))) |
begin |
if (isrc) |
assert(lastpc == r_pc); |
if ((i_rst)||(i_clear_cache)||(o_wb_cyc)) |
o_illegal <= 1'b0; |
else |
assert(lastpc + 4== r_pc); |
end |
o_illegal <= (illegal_valid) |
&&(illegal_cache == i_pc[(AW-1):PW]); |
|
always @(posedge i_clk) |
if (i_new_pc) |
f_next_pc <= { i_pc[AW+1:2] + 1'b1, 2'b00 }; |
else if ((i_stall_n)&&(r_v)) |
f_next_pc <= { i_pc[AW+1:2] + 1'b1, 2'b00 }; |
always @(*) |
if (!i_new_pc) |
`ASSUME(i_pc == f_next_pc); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(o_valid)&&($past(o_valid)) |
&&(!$past(i_reset)) |
&&(!$past(i_new_pc)) |
&&(!$past(i_stall_n)) |
&&(!o_illegal)) |
begin |
assert(cache_tags[o_pc[(CW+1):LS+2]] == o_pc[(AW+1):CW+2]); |
end |
|
// |
// If an instruction is accepted, we should *always* move on to another |
// instruction. The only exception is following an i_new_pc (or |
// other invalidator), at which point the next instruction should |
// be invalid. |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_valid))&&($past(i_stall_n))) |
begin |
// Should always advance the instruction |
assert((!o_valid)||(o_pc != $past(o_pc))); |
end |
|
// |
// Once an instruction becomes valid, it should never become invalid |
// unless there's been a request for a new instruction. |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(!i_reset && !i_clear_cache && !i_new_pc)) |
&&($past(o_valid && !i_stall_n)) |
&&(!i_new_pc)) |
begin |
if ((!$past(o_illegal))&&(!$past(o_wb_cyc && i_wb_err))) |
begin |
assert(o_valid); |
assert($stable(o_illegal)); |
assert($stable(o_insn)); |
end else |
assert((o_illegal)||(!o_valid)); |
end |
`ifdef PFCACHE |
///////////////////////////////////////////////////// |
// |
// |
// Assertions associated with a response to a known |
// address request |
// |
// |
///////////////////////////////////////////////////// |
|
|
(* anyconst *) reg [AW:0] f_const_addr; |
(* anyconst *) reg [BUSW-1:0] f_const_insn; |
|
wire f_this_pc, f_this_insn, f_this_data, f_this_line, |
f_this_ack, f_this_tag; // f_this_addr; |
assign f_this_pc = (o_pc == { f_const_addr[AW-1:0], 2'b00 }); |
// assign f_this_addr = (o_wb_addr == f_const_addr[AW-1:0] ); |
assign f_this_insn = (o_insn == f_const_insn); |
assign f_this_data = (i_wb_data == f_const_insn); |
assign f_this_line = (o_wb_addr[AW-1:LS] == f_const_addr[AW-1:LS]); |
assign f_this_ack = (f_this_line)&&(f_nacks == f_const_addr[LS-1:0]); |
assign f_this_tag = (tagval == f_const_addr[AW-1:CW]); |
|
always @(posedge i_clk) |
if ((o_valid)&&(f_this_pc)&&(!$past(o_illegal))) |
begin |
assert(o_illegal == f_const_addr[AW]); |
if (!o_illegal) |
begin |
assert(f_this_insn); |
assert(f_this_tag); |
end |
end |
|
always @(*) |
if ((valid_mask[f_const_addr[CW-1:LS]]) |
&&(cache_tags[f_const_addr[(CW-1):LS]]==f_const_addr[AW-1:CW])) |
assert(f_const_insn == cache[f_const_addr[CW-1:0]]); |
else if ((o_wb_cyc)&&(o_wb_addr[AW-1:LS] == f_const_addr[AW-1:LS]) |
&&(f_nacks > f_const_addr[LS-1:0])) |
begin |
assert(f_const_insn == cache[f_const_addr[CW-1:0]]); |
end |
|
always @(*) |
if (o_wb_cyc) |
assert(wraddr[CW-1:LS] == o_wb_addr[CW-1:LS]); |
|
always @(*) |
if (!f_const_addr[AW]) |
assert((!illegal_valid) |
||(illegal_cache != f_const_addr[AW-1:LS])); |
else |
assert((cache_tags[f_const_addr[CW-1:LS]]!=f_const_addr[AW-1:CW]) |
||(!valid_mask[f_const_addr[CW-1:LS]])); |
|
always @(*) |
if ((f_this_line)&&(o_wb_cyc)) |
begin |
if (f_const_addr[AW]) |
assume(!i_wb_ack); |
else |
assume(!i_wb_err); |
|
if ((f_this_ack)&&(i_wb_ack)) |
assume(f_this_data); |
end |
|
always @(*) |
if ((f_this_line)&&(!f_const_addr[AW])) |
assume(!i_wb_err); |
|
always @(*) |
if (!f_const_addr[AW]) |
assume((!valid_mask[f_const_addr[CW-1:LS]]) |
||(cache_tags[f_const_addr[CW-1:LS]] != f_const_addr[AW-1:CW])); |
`endif |
|
// |
// |
// Cover properties |
// |
// |
reg f_valid_legal; |
always @(*) |
f_valid_legal = o_valid && (!o_illegal); |
always @(posedge i_clk) // Trace 0 |
cover((o_valid)&&( o_illegal)); |
always @(posedge i_clk) // Trace 1 |
cover(f_valid_legal); |
always @(posedge i_clk) // Trace 2 |
cover((f_valid_legal) |
&&($past(!o_valid && !i_new_pc)) |
&&($past(i_new_pc,2))); |
always @(posedge i_clk) // Trace 3 |
cover((f_valid_legal)&&($past(i_stall_n))&&($past(i_new_pc))); |
always @(posedge i_clk) // Trace 4 |
cover((f_valid_legal)&&($past(f_valid_legal && i_stall_n))); |
always @(posedge i_clk) // Trace 5 |
cover((f_valid_legal) |
&&($past(f_valid_legal && i_stall_n)) |
&&($past(f_valid_legal && i_stall_n,2)) |
&&($past(f_valid_legal && i_stall_n,3))); |
always @(posedge i_clk) // Trace 6 |
cover((f_valid_legal) |
&&($past(f_valid_legal && i_stall_n)) |
&&($past(f_valid_legal && i_stall_n,2)) |
&&($past(!o_illegal && i_stall_n && i_new_pc,3)) |
&&($past(f_valid_legal && i_stall_n,4)) |
&&($past(f_valid_legal && i_stall_n,5)) |
&&($past(f_valid_legal && i_stall_n,6))); |
|
`endif // FORMAL |
endmodule |
/core/pipefetch.v
30,7 → 30,7
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015,2017,2019 Gisselquist Technology, LLC |
// Copyright (C) 2015,2017, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
54,8 → 54,6
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
// |
module pipefetch(i_clk, i_rst, i_new_pc, i_clear_cache, i_stall_n, i_pc, |
o_i, o_pc, o_v, |
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, |
64,9 → 62,9
parameter RESET_ADDRESS=32'h0010_0000, |
LGCACHELEN = 6, ADDRESS_WIDTH=24, |
CACHELEN=(1<<LGCACHELEN), BUSW=32, AW=ADDRESS_WIDTH; |
input wire i_clk, i_rst, i_new_pc, |
input i_clk, i_rst, i_new_pc, |
i_clear_cache, i_stall_n; |
input wire [(AW-1):0] i_pc; |
input [(AW-1):0] i_pc; |
output reg [(BUSW-1):0] o_i; |
output reg [(AW-1):0] o_pc; |
output wire o_v; |
76,11 → 74,11
output reg [(AW-1):0] o_wb_addr; |
output wire [(BUSW-1):0] o_wb_data; |
// |
input wire i_wb_ack, i_wb_stall, i_wb_err; |
input wire [(BUSW-1):0] i_wb_data; |
input i_wb_ack, i_wb_stall, i_wb_err; |
input [(BUSW-1):0] i_wb_data; |
// |
// Is the (data) memory unit also requesting access to the bus? |
input wire i_wb_request; |
input i_wb_request; |
output wire o_illegal; |
|
// Fixed bus outputs: we read from the bus only, never write. |
122,7 → 120,7
+(3<<(LGCACHELEN-2))) |
&&(|r_nvalid[(LGCACHELEN):(LGCACHELEN-1)]); |
|
initial r_cache_base = RESET_ADDRESS[(AW+1):2]; |
initial r_cache_base = RESET_ADDRESS; |
always @(posedge i_clk) |
begin |
if ((i_rst)||(i_clear_cache)||((o_wb_cyc)&&(i_wb_err))) |
303,4 → 301,5
|
assign o_illegal = (o_pc == ill_address)&&(~i_rst)&&(~i_new_pc)&&(~i_clear_cache); |
|
|
endmodule |
/core/pipemem.v
17,7 → 17,7
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2019, Gisselquist Technology, LLC |
// Copyright (C) 2015-2017, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
41,35 → 41,22
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
// |
module pipemem(i_clk, i_reset, i_pipe_stb, i_lock, |
module pipemem(i_clk, i_rst, i_pipe_stb, i_lock, |
i_op, i_addr, i_data, i_oreg, |
o_busy, o_pipe_stalled, o_valid, o_err, o_wreg, o_result, |
o_wb_cyc_gbl, o_wb_cyc_lcl, |
o_wb_stb_gbl, o_wb_stb_lcl, |
o_wb_we, o_wb_addr, o_wb_data, o_wb_sel, |
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data |
`ifdef FORMAL |
, f_nreqs, f_nacks, f_outstanding, f_pc |
`endif |
); |
parameter ADDRESS_WIDTH=30; |
parameter [0:0] IMPLEMENT_LOCK=1'b1, |
WITH_LOCAL_BUS=1'b1, |
OPT_ZERO_ON_IDLE=1'b0, |
// OPT_ALIGNMENT_ERR |
OPT_ALIGNMENT_ERR=1'b0; |
localparam AW=ADDRESS_WIDTH, |
FLN=4; |
parameter [(FLN-1):0] OPT_MAXDEPTH=4'hd; |
input wire i_clk, i_reset; |
input wire i_pipe_stb, i_lock; |
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data); |
parameter ADDRESS_WIDTH=30, IMPLEMENT_LOCK=0; |
localparam AW=ADDRESS_WIDTH; |
input i_clk, i_rst; |
input i_pipe_stb, i_lock; |
// CPU interface |
input wire [2:0] i_op; |
input wire [31:0] i_addr; |
input wire [31:0] i_data; |
input wire [4:0] i_oreg; |
input [2:0] i_op; |
input [31:0] i_addr; |
input [31:0] i_data; |
input [4:0] i_oreg; |
// CPU outputs |
output wire o_busy; |
output wire o_pipe_stalled; |
86,201 → 73,134
output reg [31:0] o_wb_data; |
output reg [3:0] o_wb_sel; |
// Wishbone inputs |
input wire i_wb_ack, i_wb_stall, i_wb_err; |
input wire [31:0] i_wb_data; |
// Formal |
parameter F_LGDEPTH=5; |
`ifdef FORMAL |
output wire [(F_LGDEPTH-1):0] f_nreqs, f_nacks, f_outstanding; |
output reg f_pc; |
`endif |
input i_wb_ack, i_wb_stall, i_wb_err; |
input [31:0] i_wb_data; |
|
|
reg cyc; |
reg r_wb_cyc_gbl, r_wb_cyc_lcl, fifo_full; |
reg [(FLN-1):0] rdaddr, wraddr; |
wire [(FLN-1):0] nxt_rdaddr, fifo_fill; |
reg [(3+5-1):0] fifo_oreg [0:15]; |
reg fifo_gie; |
reg cyc; |
reg r_wb_cyc_gbl, r_wb_cyc_lcl; |
reg [3:0] rdaddr, wraddr; |
wire [3:0] nxt_rdaddr; |
reg [(4+5-1):0] fifo_oreg [0:15]; |
initial rdaddr = 0; |
initial wraddr = 0; |
|
reg misaligned; |
|
always @(*) |
if (OPT_ALIGNMENT_ERR) |
begin |
casez({ i_op[2:1], i_addr[1:0] }) |
4'b01?1: misaligned = i_pipe_stb; |
4'b0110: misaligned = i_pipe_stb; |
4'b10?1: misaligned = i_pipe_stb; |
default: misaligned = i_pipe_stb; |
endcase |
end else |
misaligned = 1'b0; |
|
always @(posedge i_clk) |
fifo_oreg[wraddr] <= { i_oreg[3:0], i_op[2:1], i_addr[1:0] }; |
fifo_oreg[wraddr] <= { i_oreg, i_op[2:1], i_addr[1:0] }; |
|
always @(posedge i_clk) |
if (i_pipe_stb) |
fifo_gie <= i_oreg[4]; |
|
initial wraddr = 0; |
always @(posedge i_clk) |
if (i_reset) |
wraddr <= 0; |
else if (((i_wb_err)&&(cyc))||((i_pipe_stb)&&(misaligned))) |
if ((i_rst)||(i_wb_err)) |
wraddr <= 0; |
else if (i_pipe_stb) |
wraddr <= wraddr + 1'b1; |
|
initial rdaddr = 0; |
else if (i_pipe_stb) |
wraddr <= wraddr + 1'b1; |
always @(posedge i_clk) |
if (i_reset) |
rdaddr <= 0; |
else if (((i_wb_err)&&(cyc))||((i_pipe_stb)&&(misaligned))) |
rdaddr <= 0; |
else if ((i_wb_ack)&&(cyc)) |
rdaddr <= rdaddr + 1'b1; |
|
assign fifo_fill = wraddr - rdaddr; |
|
initial fifo_full = 0; |
always @(posedge i_clk) |
if (i_reset) |
fifo_full <= 0; |
else if (((i_wb_err)&&(cyc))||((i_pipe_stb)&&(misaligned))) |
fifo_full <= 0; |
else if (i_pipe_stb) |
fifo_full <= (fifo_fill >= OPT_MAXDEPTH-1); |
else |
fifo_full <= (fifo_fill >= OPT_MAXDEPTH); |
|
if ((i_rst)||(i_wb_err)) |
rdaddr <= 0; |
else if ((i_wb_ack)&&(cyc)) |
rdaddr <= rdaddr + 1'b1; |
assign nxt_rdaddr = rdaddr + 1'b1; |
|
wire gbl_stb, lcl_stb, lcl_bus; |
assign lcl_bus = (i_addr[31:24]==8'hff)&&(WITH_LOCAL_BUS); |
assign lcl_stb = (lcl_bus)&&(!misaligned); |
assign gbl_stb = ((!lcl_bus)||(!WITH_LOCAL_BUS))&&(!misaligned); |
wire gbl_stb, lcl_stb; |
assign lcl_stb = (i_addr[31:24]==8'hff); |
assign gbl_stb = (~lcl_stb); |
//= ((i_addr[31:8]!=24'hc00000)||(i_addr[7:5]!=3'h0)); |
|
initial cyc = 0; |
initial r_wb_cyc_lcl = 0; |
initial r_wb_cyc_gbl = 0; |
initial o_wb_stb_lcl = 0; |
initial o_wb_stb_gbl = 0; |
always @(posedge i_clk) |
if (i_reset) |
begin |
r_wb_cyc_gbl <= 1'b0; |
r_wb_cyc_lcl <= 1'b0; |
o_wb_stb_gbl <= 1'b0; |
o_wb_stb_lcl <= 1'b0; |
cyc <= 1'b0; |
end else if (cyc) |
begin |
if (((!i_wb_stall)&&(!i_pipe_stb)&&(!misaligned)) |
||(i_wb_err)) |
if (i_rst) |
begin |
o_wb_stb_gbl <= 1'b0; |
o_wb_stb_lcl <= 1'b0; |
end |
|
if (((i_wb_ack)&&(nxt_rdaddr == wraddr) |
&&((!i_pipe_stb)||(misaligned))) |
||(i_wb_err)) |
begin |
r_wb_cyc_gbl <= 1'b0; |
r_wb_cyc_lcl <= 1'b0; |
o_wb_stb_gbl <= 1'b0; |
o_wb_stb_lcl <= 1'b0; |
cyc <= 1'b0; |
end else if (cyc) |
begin |
if ((~i_wb_stall)&&(~i_pipe_stb)) |
begin |
o_wb_stb_gbl <= 1'b0; |
o_wb_stb_lcl <= 1'b0; |
// end else if ((i_pipe_stb)&&(~i_wb_stall)) |
// begin |
// o_wb_addr <= i_addr[(AW-1):0]; |
// o_wb_data <= i_data; |
end |
|
if (((i_wb_ack)&&(nxt_rdaddr == wraddr))||(i_wb_err)) |
begin |
r_wb_cyc_gbl <= 1'b0; |
r_wb_cyc_lcl <= 1'b0; |
cyc <= 1'b0; |
end |
end else if (i_pipe_stb) // New memory operation |
begin // Grab the wishbone |
r_wb_cyc_lcl <= lcl_stb; |
r_wb_cyc_gbl <= gbl_stb; |
o_wb_stb_lcl <= lcl_stb; |
o_wb_stb_gbl <= gbl_stb; |
cyc <= 1'b1; |
// o_wb_addr <= i_addr[(AW-1):0]; |
// o_wb_data <= i_data; |
// o_wb_we <= i_op |
end |
end else if (i_pipe_stb) // New memory operation |
begin // Grab the wishbone |
r_wb_cyc_lcl <= lcl_stb; |
r_wb_cyc_gbl <= gbl_stb; |
o_wb_stb_lcl <= lcl_stb; |
o_wb_stb_gbl <= gbl_stb; |
cyc <= (!misaligned); |
end |
|
always @(posedge i_clk) |
if ((!cyc)||(!i_wb_stall)) |
begin |
if ((OPT_ZERO_ON_IDLE)&&(!i_pipe_stb)) |
o_wb_addr <= 0; |
else |
if ((!cyc)||(!i_wb_stall)) |
begin |
o_wb_addr <= i_addr[(AW+1):2]; |
if (!i_op[0]) // Always select everything on reads |
o_wb_sel <= 4'b1111; // Op is even |
else casez({ i_op[2:1], i_addr[1:0] }) |
4'b100?: o_wb_sel <= 4'b1100; // Op = 5 |
4'b101?: o_wb_sel <= 4'b0011; // Op = 5 |
4'b1100: o_wb_sel <= 4'b1000; // Op = 5 |
4'b1101: o_wb_sel <= 4'b0100; // Op = 7 |
4'b1110: o_wb_sel <= 4'b0010; // Op = 7 |
4'b1111: o_wb_sel <= 4'b0001; // Op = 7 |
default: o_wb_sel <= 4'b1111; // Op = 7 |
endcase |
|
if ((OPT_ZERO_ON_IDLE)&&(!i_pipe_stb)) |
o_wb_sel <= 4'b0000; |
else casez({ i_op[2:1], i_addr[1:0] }) |
4'b100?: o_wb_sel <= 4'b1100; // Op = 5 |
4'b101?: o_wb_sel <= 4'b0011; // Op = 5 |
4'b1100: o_wb_sel <= 4'b1000; // Op = 5 |
4'b1101: o_wb_sel <= 4'b0100; // Op = 7 |
4'b1110: o_wb_sel <= 4'b0010; // Op = 7 |
4'b1111: o_wb_sel <= 4'b0001; // Op = 7 |
default: o_wb_sel <= 4'b1111; // Op = 7 |
endcase |
casez({ i_op[2:1], i_addr[1:0] }) |
4'b100?: o_wb_data <= { i_data[15:0], 16'h00 }; |
4'b101?: o_wb_data <= { 16'h00, i_data[15:0] }; |
4'b1100: o_wb_data <= { i_data[7:0], 24'h00 }; |
4'b1101: o_wb_data <= { 8'h00, i_data[7:0], 16'h00 }; |
4'b1110: o_wb_data <= { 16'h00, i_data[7:0], 8'h00 }; |
4'b1111: o_wb_data <= { 24'h00, i_data[7:0] }; |
default: o_wb_data <= i_data; |
endcase |
|
if ((OPT_ZERO_ON_IDLE)&&(!i_pipe_stb)) |
o_wb_data <= 0; |
else casez({ i_op[2:1], i_addr[1:0] }) |
4'b100?: o_wb_data <= { i_data[15:0], 16'h00 }; |
4'b101?: o_wb_data <= { 16'h00, i_data[15:0] }; |
4'b1100: o_wb_data <= { i_data[7:0], 24'h00 }; |
4'b1101: o_wb_data <= { 8'h00, i_data[7:0], 16'h00 }; |
4'b1110: o_wb_data <= { 16'h00, i_data[7:0], 8'h00 }; |
4'b1111: o_wb_data <= { 24'h00, i_data[7:0] }; |
default: o_wb_data <= i_data; |
endcase |
end |
end |
|
always @(posedge i_clk) |
if ((i_pipe_stb)&&(!cyc)) |
o_wb_we <= i_op[0]; |
else if ((OPT_ZERO_ON_IDLE)&&(!cyc)) |
o_wb_we <= 1'b0; |
if ((i_pipe_stb)&&(~cyc)) |
o_wb_we <= i_op[0]; |
|
initial o_valid = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
o_valid <= 1'b0; |
else |
o_valid <= (cyc)&&(i_wb_ack)&&(!o_wb_we); |
|
o_valid <= (cyc)&&(i_wb_ack)&&(~o_wb_we); |
initial o_err = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
o_err <= 1'b0; |
else |
o_err <= ((cyc)&&(i_wb_err))||((i_pipe_stb)&&(misaligned)); |
o_err <= (cyc)&&(i_wb_err); |
assign o_busy = cyc; |
|
wire [7:0] w_wreg; |
wire [8:0] w_wreg; |
assign w_wreg = fifo_oreg[rdaddr]; |
always @(posedge i_clk) |
o_wreg <= { fifo_gie, w_wreg[7:4] }; |
o_wreg <= w_wreg[8:4]; |
always @(posedge i_clk) |
if ((OPT_ZERO_ON_IDLE)&&((!cyc)||((!i_wb_ack)&&(!i_wb_err)))) |
o_result <= 0; |
else begin |
casez(w_wreg[3:0]) |
4'b1100: o_result <= { 24'h00, i_wb_data[31:24] }; |
4'b1101: o_result <= { 24'h00, i_wb_data[23:16] }; |
4'b1110: o_result <= { 24'h00, i_wb_data[15: 8] }; |
4'b1111: o_result <= { 24'h00, i_wb_data[ 7: 0] }; |
4'b100?: o_result <= { 16'h00, i_wb_data[31:16] }; |
4'b101?: o_result <= { 16'h00, i_wb_data[15: 0] }; |
default: o_result <= i_wb_data[31:0]; |
4'b1100: o_result = { 24'h00, i_wb_data[31:24] }; |
4'b1101: o_result = { 24'h00, i_wb_data[23:16] }; |
4'b1110: o_result = { 24'h00, i_wb_data[15: 8] }; |
4'b1111: o_result = { 24'h00, i_wb_data[ 7: 0] }; |
4'b100?: o_result = { 16'h00, i_wb_data[31:16] }; |
4'b101?: o_result = { 16'h00, i_wb_data[15: 0] }; |
default: o_result = i_wb_data[31:0]; |
endcase |
end |
|
assign o_pipe_stalled = ((cyc)&&(fifo_full))||((cyc) |
&&((i_wb_stall)||((!o_wb_stb_lcl)&&(!o_wb_stb_gbl)))); |
assign o_pipe_stalled = (cyc) |
&&((i_wb_stall)||((~o_wb_stb_lcl)&&(~o_wb_stb_gbl))); |
|
generate |
if (IMPLEMENT_LOCK != 0) |
290,12 → 210,7
initial lock_gbl = 1'b0; |
initial lock_lcl = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||((i_wb_err)&&(cyc)) |
||((i_pipe_stb)&&(misaligned))) |
begin |
lock_gbl <= 1'b0; |
lock_lcl <= 1'b0; |
end else begin |
lock_gbl <= (i_lock)&&((r_wb_cyc_gbl)||(lock_gbl)); |
lock_lcl <= (i_lock)&&((r_wb_cyc_lcl)||(lock_lcl)); |
end |
302,319 → 217,10
|
assign o_wb_cyc_gbl = (r_wb_cyc_gbl)||(lock_gbl); |
assign o_wb_cyc_lcl = (r_wb_cyc_lcl)||(lock_lcl); |
|
end else begin |
assign o_wb_cyc_gbl = (r_wb_cyc_gbl); |
assign o_wb_cyc_lcl = (r_wb_cyc_lcl); |
end endgenerate |
|
// Make verilator happy |
// verilator lint_off UNUSED |
wire unused; |
assign unused = i_lock; |
// verilator lint_on UNUSED |
|
`ifdef FORMAL |
`define ASSERT assert |
`ifdef PIPEMEM |
`define ASSUME assume |
`else |
`define ASSUME assert |
`endif |
|
reg f_past_valid; |
initial f_past_valid = 0; |
always @(posedge i_clk) |
f_past_valid = 1'b1; |
always @(*) |
if (!f_past_valid) |
`ASSUME(i_reset); |
|
initial `ASSUME( i_reset); |
initial `ASSUME(!i_pipe_stb); |
|
wire f_cyc, f_stb; |
assign f_cyc = cyc; |
assign f_stb = (o_wb_stb_gbl)||(o_wb_stb_lcl); |
|
`ifdef PIPEMEM |
`define MASTER fwb_master |
`else |
`define MASTER fwb_counter |
`endif |
`MASTER #(.AW(AW), .F_LGDEPTH(F_LGDEPTH), |
// .F_MAX_REQUESTS(14), // Not quite true, can do more |
.F_OPT_RMW_BUS_OPTION(IMPLEMENT_LOCK), |
.F_OPT_DISCONTINUOUS(IMPLEMENT_LOCK)) |
fwb(i_clk, i_reset, |
cyc, f_stb, o_wb_we, o_wb_addr, o_wb_data, o_wb_sel, |
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err, |
f_nreqs, f_nacks, f_outstanding); |
|
|
// |
// Assumptions about inputs |
// |
always @(posedge i_clk) |
if ((!f_past_valid)||($past(i_reset))) |
begin |
`ASSERT(!o_err); |
`ASSERT(!o_busy); |
`ASSERT(!o_pipe_stalled); |
`ASSERT(!o_valid); |
end |
|
always @(posedge i_clk) |
if (o_pipe_stalled) |
`ASSUME(!i_pipe_stb); |
|
// On any pipe request, the new address is the same or plus one |
always @(posedge i_clk) |
if ((f_past_valid)&&(f_cyc)&&(!i_wb_stall)&&(i_pipe_stb)) |
begin |
`ASSUME( (i_addr[(AW+1):2] == o_wb_addr) |
||(i_addr[(AW+1):2] == o_wb_addr+1)); |
`ASSUME(i_op[0] == o_wb_we); |
end |
|
always @(posedge i_clk) |
if ((r_wb_cyc_gbl)&&(i_pipe_stb)) |
`ASSUME(gbl_stb); |
|
always @(posedge i_clk) |
if ((r_wb_cyc_lcl)&&(i_pipe_stb)) |
`ASSUME(lcl_stb); |
|
// If stb is false, then either lock is on or there are no more STB's |
always @(posedge i_clk) |
if ((f_cyc)&&(!f_stb)) |
`ASSUME((i_lock)||(!i_pipe_stb)); |
|
//always @(posedge i_clk) |
// if ((f_past_valid)&&($past(f_cyc))&&(!$past(i_lock))) |
// `ASSUME(!i_lock); |
|
wire [3:0] f_pipe_used; |
assign f_pipe_used = wraddr - rdaddr; |
always @(*) |
`ASSERT(f_pipe_used == fifo_fill); |
always @(posedge i_clk) |
if (f_pipe_used == OPT_MAXDEPTH) |
|
begin |
`ASSUME(!i_pipe_stb); |
`ASSERT((o_busy)&&(o_pipe_stalled)); |
end |
|
always @(*) |
`ASSERT(fifo_fill <= OPT_MAXDEPTH); |
|
always @(*) |
if (!IMPLEMENT_LOCK) |
`ASSUME(!i_lock); |
|
`ifndef VERILATOR |
always @(*) |
if ((WITH_LOCAL_BUS)&&(o_wb_cyc_gbl|o_wb_cyc_lcl) |
&&(i_pipe_stb)) |
begin |
if (o_wb_cyc_lcl) |
// `ASSUME(i_addr[31:24] == 8'hff); |
assume(i_addr[31:24] == 8'hff); |
else |
assume(i_addr[31:24] != 8'hff); |
end |
`endif |
|
always @(*) |
if (!WITH_LOCAL_BUS) |
begin |
assert(!r_wb_cyc_lcl); |
assert(!o_wb_cyc_lcl); |
assert(!o_wb_stb_lcl); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(f_cyc))&&(!$past(i_pipe_stb))) |
`ASSERT(f_pipe_used == 0); |
|
always @(*) |
if (!f_cyc) |
`ASSERT(f_pipe_used == 0); |
|
always @(posedge i_clk) |
if (f_pipe_used >= 13) |
`ASSUME(!i_pipe_stb); |
|
always @(posedge i_clk) |
if ((f_cyc)&&(f_pipe_used >= 13)) |
`ASSERT((o_busy)&&(o_pipe_stalled)); |
|
|
always @(posedge i_clk) |
`ASSERT((!r_wb_cyc_gbl)||(!r_wb_cyc_lcl)); |
|
always @(posedge i_clk) |
`ASSERT((!o_wb_cyc_gbl)||(!o_wb_cyc_lcl)); |
|
always @(posedge i_clk) |
`ASSERT((!o_wb_stb_gbl)||(!o_wb_stb_lcl)); |
|
always @(*) |
if (!WITH_LOCAL_BUS) |
begin |
assert(!o_wb_cyc_lcl); |
assert(!o_wb_stb_lcl); |
if (o_wb_stb_lcl) |
assert(o_wb_addr[(AW-1):22] == {(8-(30-AW)){1'b1}}); |
end |
|
always @(posedge i_clk) |
if (o_wb_stb_gbl) |
`ASSERT(o_wb_cyc_gbl); |
|
always @(posedge i_clk) |
if (o_wb_stb_lcl) |
`ASSERT(o_wb_cyc_lcl); |
|
always @(posedge i_clk) |
`ASSERT(cyc == (r_wb_cyc_gbl|r_wb_cyc_lcl)); |
|
always @(posedge i_clk) |
`ASSERT(cyc == (r_wb_cyc_lcl)|(r_wb_cyc_gbl)); |
always @(posedge i_clk) |
if ((f_past_valid)&&(!i_reset)&&(!$past(misaligned))) |
begin |
if (f_stb) |
`ASSERT(f_pipe_used == f_outstanding + 4'h1); |
else |
`ASSERT(f_pipe_used == f_outstanding); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(r_wb_cyc_gbl||r_wb_cyc_lcl)) |
&&(!$past(f_stb))) |
`ASSERT(!f_stb); |
|
always @(*) |
`ASSERT((!lcl_stb)||(!gbl_stb)); |
|
reg [(1<<FLN)-1:0] f_mem_used; |
wire [(1<<FLN)-1:0] f_zero; |
// |
// insist that we only ever accept memory requests for the same GIE |
// (i.e. 4th bit of register) |
// |
always @(*) |
if ((i_pipe_stb)&&(wraddr != rdaddr)) |
`ASSUME(i_oreg[4] == fifo_gie); |
|
initial f_pc = 1'b0; |
always @(posedge i_clk) |
if(i_reset) |
f_pc <= 1'b0; |
else if (i_pipe_stb) |
f_pc <= (((f_pc)&&(f_cyc)) |
||((!i_op[0])&&(i_oreg[3:1] == 3'h7))); |
else if (!f_cyc) |
f_pc <= 1'b0; |
|
always @(posedge i_clk) |
if ((f_cyc)&&(o_wb_we)) |
`ASSERT(!f_pc); |
|
always @(*) |
if ((f_pc)&&(f_cyc)) |
`ASSUME(!i_pipe_stb); |
|
always @(*) |
if (wraddr == rdaddr) |
begin |
`ASSERT(!r_wb_cyc_gbl); |
`ASSERT(!r_wb_cyc_lcl); |
end else if (f_cyc) |
begin |
`ASSERT(fifo_fill == f_outstanding + ((f_stb)?1:0)); |
end |
|
|
`define FIFOCHECK |
`ifdef FIFOCHECK |
wire [3:0] lastaddr = wraddr - 1'b1; |
|
integer k; |
always @(*) |
begin |
f_mem_used = 0; |
for(k = 0 ; k < (1<<FLN); k=k+1) |
begin |
if (wraddr == rdaddr) |
f_mem_used[k] = 1'b0; |
else if (wraddr > rdaddr) |
begin |
if ((k < wraddr)&&(k >= rdaddr)) |
f_mem_used[k] = 1'b1; |
end else if (k < wraddr) |
f_mem_used[k] = 1'b1; |
else if (k >= rdaddr) |
f_mem_used[k] = 1'b1; |
end |
end |
|
|
always @(*) |
begin |
for(k=0; k<(1<<FLN); k=k+1) |
if ((f_mem_used[k])&&(!o_wb_we)&&((!f_pc)||(k!=lastaddr))) |
`ASSERT(fifo_oreg[k][7:5] != 3'h7); |
end |
|
initial assert(!fifo_full); |
|
always @(posedge i_clk) |
cover(cyc && !fifo_full); |
|
always @(posedge i_clk) |
cover((f_cyc)&&(f_stb)&&(!i_wb_stall)&&(!i_wb_ack) |
&&(!o_pipe_stalled)); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(f_stb))&&($past(f_cyc))) |
cover((f_cyc)&&(i_wb_ack)); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(f_stb))&&($past(f_cyc))) |
cover($past(i_wb_ack)&&(i_wb_ack)); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_valid))) |
cover(o_valid); |
|
`endif // FIFOCHECK |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(f_past_valid))&&($past(f_cyc))&&($past(f_cyc,2))) |
`ASSERT($stable(o_wreg[4])); |
|
always @(*) |
`ASSERT((!f_cyc)||(!o_valid)||(o_wreg[3:1]!=3'h7)); |
|
`endif // FORMAL |
endmodule |
// |
// |
// Usage (from yosys): (Before) (A,!OPTZ) (A,OPTZ) |
// Cells: 302 314 391 |
// FDRE 138 140 140 |
// LUT1 2 2 2 |
// LUT2 38 41 61 |
// LUT3 13 16 33 |
// LUT4 3 8 12 |
// LUT5 22 10 8 |
// LUT6 52 59 81 |
// MUXCY 6 6 6 |
// MUXF7 10 13 21 |
// MUXF8 1 2 10 |
// RAM64X1D 9 9 9 |
// XORCY 8 8 8 |
// |
// |
core
Property changes :
Deleted: svn:ignore
## -1 +0,0 ##
-obj_dir
Index: cpudefs.v
===================================================================
--- cpudefs.v (revision 209)
+++ cpudefs.v (revision 205)
@@ -28,7 +28,7 @@
//
////////////////////////////////////////////////////////////////////////////////
//
-// Copyright (C) 2015-2019, Gisselquist Technology, LLC
+// Copyright (C) 2015-2016, Gisselquist Technology, LLC
//
// This program is free software (firmware): you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
@@ -60,7 +60,22 @@
// it handles various instructions within the set:
//
//
+// OPT_ILLEGAL_INSTRUCTION is part of a new section of code that is supposed
+// to recognize illegal instructions and interrupt the CPU whenever one such
+// instruction is encountered. The goal is to create a soft floating point
+// unit via this approach, that can then be replaced with a true floating point
+// unit. As I'm not there yet, it just catches illegal instructions and
+// interrupts the CPU on any such instruction--when defined. Otherwise,
+// illegal instructions are quietly ignored and their behaviour is ...
+// undefined. (Many get treated like NOOPs ...)
//
+// I recommend setting this flag so highly, that I'm likely going to remove
+// the option to turn this off in future versions of this CPU.
+//
+`define OPT_ILLEGAL_INSTRUCTION
+//
+//
+//
// OPT_MULTIPLY controls whether or not the multiply is built and included
// in the ALU by default. Set this option and a parameter will be set that
// includes the multiply. (This parameter may still be overridden, as with
@@ -224,15 +239,6 @@
//
//
//
-// OPT_DCACHE enables a CPU data cache for (hopefully) better performance
-// in terms of speed. It requires telling the CPU which parts of memory
-// can be cachable in terms of three separate address regions: one for the
-// SDRAM, one for the flash, and another for the block RAM.
-//
-`define OPT_DCACHE
-//
-//
-//
// OPT_PIPELINED_BUS_ACCESS controls whether or not LOD/STO instructions
// can take advantaged of pipelined bus instructions. To be eligible, the
// operations must be identical (cannot pipeline loads and stores, just loads
@@ -255,13 +261,7 @@
`endif // OPT_SINGLE_FETCH
//
//
-// [EXPERIMENTAL--and not (yet) finished]
-// OPT_MMU determines whether or not an MMU will be included in the ZipSystem
-// containing the ZipCPU. When set, the ZipCPU will route all memory accesses
-// through the MMU as an address translator, creating a form of Virtual memory.
//
-// `define OPT_MMU
-//
// Now let's talk about peripherals for a moment. These next two defines
// control whether the DMA controller is included in the Zip System, and
// whether or not the 8 accounting timers are also included. Set these to
Index: Makefile
===================================================================
--- Makefile (revision 209)
+++ Makefile (revision 205)
@@ -1,48 +1,42 @@
################################################################################
-##
-## Filename: Makefile
-##
-## Project: Zip CPU -- a small, lightweight, RISC CPU soft core
-##
-## Purpose: This makefile builds a verilator simulation of the zipsystem.
-## It does not make the system within Vivado or Quartus.
-##
-##
-## Creator: Dan Gisselquist, Ph.D.
-## Gisselquist Technology, LLC
-##
+#
+# Filename: Makefile
+#
+# Project: Zip CPU -- a small, lightweight, RISC CPU soft core
+#
+# Purpose: This makefile builds a verilator simulation of the zipsystem.
+# It does not make the system within Vivado or Quartus.
+#
+#
+# Creator: Dan Gisselquist, Ph.D.
+# Gisselquist Technology, LLC
+#
################################################################################
-##
-## Copyright (C) 2015-2017, Gisselquist Technology, LLC
-##
-## This program is free software (firmware): you can redistribute it and/or
-## modify it under the terms of the GNU General Public License as published
-## by the Free Software Foundation, either version 3 of the License, or (at
-## your option) any later version.
-##
-## This program is distributed in the hope that it will be useful, but WITHOUT
-## ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
-## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
-## for more details.
-##
-## You should have received a copy of the GNU General Public License along
-## with this program. (It's in the $(ROOT)/doc directory. Run make with no
-## target there if the PDF file isn't present.) If not, see
-## for a copy.
-##
-## License: GPL, v3, as defined and found on www.gnu.org,
-## http://www.gnu.org/licenses/gpl.html
-##
-##
+#
+# Copyright (C) 2015-2017, Gisselquist Technology, LLC
+#
+# This program is free software (firmware): you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or (at
+# your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTIBILITY or
+# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+# for more details.
+#
+# License: GPL, v3, as defined and found on www.gnu.org,
+# http://www.gnu.org/licenses/gpl.html
+#
+#
################################################################################
-##
-##
+#
.PHONY: all
all: zipsystem zipbones cpudefs.h div zipmmu cpuops pfcache
CORED:= core
PRPHD:= peripherals
-EXD := ex
+AUXD := aux
VSRC := zipsystem.v cpudefs.v \
$(PRPHD)/wbdmac.v $(PRPHD)/icontrol.v \
$(PRPHD)/zipcounter.v $(PRPHD)/zipjiffies.v \
@@ -52,8 +46,8 @@
$(CORED)/pfcache.v \
$(CORED)/memops.v $(CORED)/pipemem.v \
$(CORED)/div.v \
- $(EXD)/busdelay.v \
- $(EXD)/wbdblpriarb.v $(EXD)/wbpriarbiter.v \
+ $(AUXD)/busdelay.v \
+ $(AUXD)/wbdblpriarb.v $(AUXD)/wbpriarbiter.v \
$(CORED)/idecode.v $(CORED)/cpuops.v
VZIP := zipbones.v cpudefs.v \
$(CORED)/zipcpu.v $(CORED)/cpuops.v $(CORED)/idecode.v \
@@ -61,20 +55,13 @@
$(CORED)/pfcache.v \
$(CORED)/memops.v $(CORED)/pipemem.v \
$(CORED)/div.v \
- $(EXD)/busdelay.v $(EXD)/wbdblpriarb.v \
+ $(AUXD)/busdelay.v $(AUXD)/wbdblpriarb.v \
$(CORED)/idecode.v $(CORED)/cpuops.v
VOBJ := obj_dir
SUBMAKE := $(MAKE) --no-print-directory --directory=$(VOBJ) -f
-ifeq ($(VERILATOR_ROOT),)
-VERILATOR := verilator
-else
-VERILATOR := $(VERILATOR_ROOT)/bin/verilator
-endif
-# VFLAGS := -Wall -MMD -D__WORDSIZE=64 --trace -cc -y $(CORED) -y $(PRPHD) -y $(EXD)
-VFLAGS := -Wall -MMD --trace -cc -y $(CORED) -y $(PRPHD) -y $(EXD)
-VERILATE=$(VERILATOR) $(VFLAGS)
+VERILATE=verilator --trace -cc -y $(CORED) -y $(PRPHD) -y $(AUXD)
$(VOBJ)/Vzipsystem.cpp: $(VSRC)
$(VERILATE) zipsystem.v
@@ -150,12 +137,3 @@
.PHONY: clean
clean:
rm -rf $(VOBJ) cpudefs.h
-
-#
-# Note Verilator's dependency created information, and include it here if we
-# can
-DEPS := $(wildcard $(VOBJ)/*.d)
-
-ifneq ($(DEPS),)
-include $(DEPS)
-endif
/peripherals/README.md
File deleted
/peripherals/icontrol.v
52,7 → 52,7
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015,2017-2019, Gisselquist Technology, LLC |
// Copyright (C) 2015,2017, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
76,23 → 76,20
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
// |
module icontrol(i_clk, i_reset, i_wr, i_data, o_data, |
module icontrol(i_clk, i_reset, i_wr, i_proc_bus, o_proc_bus, |
i_brd_ints, o_interrupt); |
parameter IUSED = 15, BW=32; |
input wire i_clk, i_reset; |
input wire i_wr; |
input wire [BW-1:0] i_data; |
output reg [BW-1:0] o_data; |
input wire [(IUSED-1):0] i_brd_ints; |
parameter IUSED = 15; |
input i_clk, i_reset; |
input i_wr; |
input [31:0] i_proc_bus; |
output wire [31:0] o_proc_bus; |
input [(IUSED-1):0] i_brd_ints; |
output wire o_interrupt; |
|
reg [(IUSED-1):0] r_int_state; |
reg [(IUSED-1):0] r_int_enable; |
wire [(IUSED-1):0] nxt_int_state; |
reg r_interrupt, r_gie; |
wire w_any; |
reg r_any, r_interrupt, r_gie; |
|
assign nxt_int_state = (r_int_state|i_brd_ints); |
initial r_int_state = 0; |
100,7 → 97,7
if (i_reset) |
r_int_state <= 0; |
else if (i_wr) |
r_int_state <= i_brd_ints | (r_int_state & (~i_data[(IUSED-1):0])); |
r_int_state <= nxt_int_state & (~i_proc_bus[(IUSED-1):0]); |
else |
r_int_state <= nxt_int_state; |
initial r_int_enable = 0; |
107,10 → 104,10
always @(posedge i_clk) |
if (i_reset) |
r_int_enable <= 0; |
else if ((i_wr)&&(i_data[BW-1])) |
r_int_enable <= r_int_enable | i_data[(16+IUSED-1):16]; |
else if ((i_wr)&&(!i_data[BW-1])) |
r_int_enable <= r_int_enable & (~ i_data[(16+IUSED-1):16]); |
else if ((i_wr)&&(i_proc_bus[31])) |
r_int_enable <= r_int_enable | i_proc_bus[(16+IUSED-1):16]; |
else if ((i_wr)&&(~i_proc_bus[31])) |
r_int_enable <= r_int_enable & (~ i_proc_bus[(16+IUSED-1):16]); |
|
initial r_gie = 1'b0; |
always @(posedge i_clk) |
117,98 → 114,46
if (i_reset) |
r_gie <= 1'b0; |
else if (i_wr) |
r_gie <= i_data[BW-1]; |
r_gie <= i_proc_bus[31]; |
|
assign w_any = ((r_int_state & r_int_enable) != 0); |
|
initial r_any = 1'b0; |
always @(posedge i_clk) |
r_any <= ((r_int_state & r_int_enable) != 0); |
initial r_interrupt = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
r_interrupt <= 1'b0; |
else |
r_interrupt <= (r_gie)&&(w_any); |
r_interrupt <= r_gie & r_any; |
|
generate |
if (IUSED < 15) |
begin |
always @(posedge i_clk) |
o_data <= { |
assign o_proc_bus = { |
r_gie, { {(15-IUSED){1'b0}}, r_int_enable }, |
w_any, { {(15-IUSED){1'b0}}, r_int_state } }; |
r_any, { {(15-IUSED){1'b0}}, r_int_state } }; |
end else begin |
always @(posedge i_clk) |
o_data <= { r_gie, r_int_enable, w_any, r_int_state }; |
assign o_proc_bus = { r_gie, r_int_enable, r_any, r_int_state }; |
end endgenerate |
|
assign o_interrupt = r_interrupt; |
|
// Make verilator happy |
// verilator lint_off UNUSED |
wire [30:0] unused; |
assign unused = i_data[30:0]; |
// verilator lint_on UNUSED |
|
`ifdef FORMAL |
`ifdef ICONTROL |
`define ASSUME assume |
`else |
`define ASSUME assert |
`endif |
|
reg f_past_valid; |
|
initial f_past_valid = 1'b0; |
/* |
reg int_condition; |
initial int_condition = 1'b0; |
initial o_interrupt_strobe = 1'b0; |
always @(posedge i_clk) |
f_past_valid <= 1'b1; |
if (i_reset) |
begin |
int_condition <= 1'b0; |
o_interrupt_strobe <= 1'b0; |
end else if (~r_interrupt) // This might end up generating |
begin // many, many, (wild many) interrupts |
int_condition <= 1'b0; |
o_interrupt_strobe <= 1'b0; |
end else if ((~int_condition)&&(r_interrupt)) |
begin |
int_condition <= 1'b1; |
o_interrupt_strobe <= 1'b1; |
end else |
o_interrupt_strobe <= 1'b0; |
*/ |
|
initial `ASSUME(i_reset); |
always @(*) |
if (!f_past_valid) |
`ASSUME(i_reset); |
assign o_interrupt = r_interrupt; |
|
always @(posedge i_clk) |
if ((!f_past_valid)||($past(i_reset))) |
begin |
assert(w_any == 0); |
assert(r_interrupt == 0); |
assert(r_gie == 0); |
assert(r_int_enable == 0); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))) |
assert((r_int_state & $past(i_brd_ints))==$past(i_brd_ints)); |
|
always @(posedge i_clk) |
if (((f_past_valid)&&(!$past(i_reset))) |
&&($past(r_int_state) & $past(r_int_enable)) |
&&($past(r_gie)) ) |
assert(o_interrupt); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(w_any))&&(!$past(i_wr))&&(!$past(i_reset))) |
assert(w_any); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(r_gie))) |
assert(!o_interrupt); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(w_any))) |
assert(!o_interrupt); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wr))) |
begin |
// Interrupts cannot be cleared, unless they are set |
assert(r_int_state == (($past(i_brd_ints)) |
|(($past(r_int_state))&(~$past(i_data[IUSED-1:0]))))); |
assert(r_gie == $past(i_data[BW-1])); |
end else if ((f_past_valid)&&(!$past(i_reset))) |
begin |
assert(r_int_state == ($past(r_int_state)|$past(i_brd_ints))); |
assert(r_gie == $past(r_gie)); |
end |
|
`endif |
endmodule |
/peripherals/wbdmac.v
56,11 → 56,7
// The minimum transfer length is one, while zero |
// is mapped to a transfer length of 1kW. |
// |
// Write 32'hffed00 to halt an ongoing transaction, completing anything |
// remaining, or 32'hafed00 to abort the current transaction leaving |
// any unfinished read/write in an undetermined state. |
// |
// |
// To use this, follow this checklist: |
// 1. Wait for any prior DMA operation to complete |
// (Read address 0, wait 'till either top bit is set or cfg_len==0) |
86,7 → 82,7
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2019, Gisselquist Technology, LLC |
// Copyright (C) 2015-2017, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
118,7 → 114,7
`define DMA_WRITE_REQ 3'b101 |
`define DMA_WRITE_ACK 3'b110 |
|
module wbdmac(i_clk, i_reset, |
module wbdmac(i_clk, i_rst, |
i_swb_cyc, i_swb_stb, i_swb_we, i_swb_addr, i_swb_data, |
o_swb_ack, o_swb_stall, o_swb_data, |
o_mwb_cyc, o_mwb_stb, o_mwb_we, o_mwb_addr, o_mwb_data, |
125,14 → 121,13
i_mwb_ack, i_mwb_stall, i_mwb_data, i_mwb_err, |
i_dev_ints, |
o_interrupt); |
parameter ADDRESS_WIDTH=30, LGMEMLEN = 10, DW=32; |
localparam LGDV=5; |
localparam AW=ADDRESS_WIDTH; |
input wire i_clk, i_reset; |
parameter ADDRESS_WIDTH=32, LGMEMLEN = 10, |
DW=32, LGDV=5,AW=ADDRESS_WIDTH; |
input i_clk, i_rst; |
// Slave/control wishbone inputs |
input wire i_swb_cyc, i_swb_stb, i_swb_we; |
input wire [1:0] i_swb_addr; |
input wire [(DW-1):0] i_swb_data; |
input i_swb_cyc, i_swb_stb, i_swb_we; |
input [1:0] i_swb_addr; |
input [(DW-1):0] i_swb_data; |
// Slave/control wishbone outputs |
output reg o_swb_ack; |
output wire o_swb_stall; |
142,11 → 137,11
output reg [(AW-1):0] o_mwb_addr; |
output reg [(DW-1):0] o_mwb_data; |
// Master/DMA wishbone responses from the bus |
input wire i_mwb_ack, i_mwb_stall; |
input wire [(DW-1):0] i_mwb_data; |
input wire i_mwb_err; |
input i_mwb_ack, i_mwb_stall; |
input [(DW-1):0] i_mwb_data; |
input i_mwb_err; |
// The interrupt device interrupt lines |
input wire [(DW-1):0] i_dev_ints; |
input [(DW-1):0] i_dev_ints; |
// An interrupt to be set upon completion |
output reg o_interrupt; |
// Need to release the bus for a higher priority user |
158,42 → 153,7
// input i_other_busmaster_requests_bus; |
// |
|
wire s_cyc, s_stb, s_we; |
wire [1:0] s_addr; |
wire [31:0] s_data; |
`define DELAY_ACCESS |
`ifdef DELAY_ACCESS |
reg r_s_cyc, r_s_stb, r_s_we; |
reg [1:0] r_s_addr; |
reg [31:0] r_s_data; |
|
always @(posedge i_clk) |
r_s_cyc <= i_swb_cyc; |
always @(posedge i_clk) |
if (i_reset) |
r_s_stb <= 1'b0; |
else |
r_s_stb <= i_swb_stb; |
always @(posedge i_clk) |
r_s_we <= i_swb_we; |
always @(posedge i_clk) |
r_s_addr<= i_swb_addr; |
always @(posedge i_clk) |
r_s_data<= i_swb_data; |
|
assign s_cyc = r_s_cyc; |
assign s_stb = r_s_stb; |
assign s_we = r_s_we; |
assign s_addr= r_s_addr; |
assign s_data= r_s_data; |
`else |
assign s_cyc = i_swb_cyc; |
assign s_stb = i_swb_stb; |
assign s_we = i_swb_we; |
assign s_addr= i_swb_addr; |
assign s_data= i_swb_data; |
`endif |
|
reg [2:0] dma_state; |
reg cfg_err, cfg_len_nonzero; |
reg [(AW-1):0] cfg_waddr, cfg_raddr, cfg_len; |
216,46 → 176,53
|
initial dma_state = `DMA_IDLE; |
initial o_interrupt = 1'b0; |
initial cfg_len = {(AW){1'b0}}; |
initial cfg_blocklen_sub_one = {(LGMEMLEN){1'b1}}; |
initial cfg_on_dev_trigger = 1'b0; |
initial cfg_len_nonzero = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
begin |
dma_state <= `DMA_IDLE; |
cfg_on_dev_trigger <= 1'b0; |
cfg_incs <= 1'b0; |
cfg_incd <= 1'b0; |
end else case(dma_state) |
case(dma_state) |
`DMA_IDLE: begin |
o_mwb_addr <= cfg_raddr; |
nwritten <= 0; |
nread <= 0; |
nracks <= 0; |
nwacks <= 0; |
cfg_len_nonzero <= (|cfg_len); |
|
// When the slave wishbone writes, and we are in this |
// (ready) configuration, then allow the DMA to be controlled |
// and thus to start. |
if ((s_stb)&&(s_we)) |
if ((i_swb_stb)&&(i_swb_we)) |
begin |
case(s_addr) |
case(i_swb_addr) |
2'b00: begin |
if ((s_data[27:16] == 12'hfed) |
&&(s_data[31:30] == 2'b00) |
if ((i_swb_data[31:16] == 16'h0fed) |
&&(cfg_len_nonzero)) |
dma_state <= `DMA_WAIT; |
cfg_blocklen_sub_one |
<= s_data[(LGMEMLEN-1):0] |
<= i_swb_data[(LGMEMLEN-1):0] |
+ {(LGMEMLEN){1'b1}}; |
// i.e. -1; |
cfg_dev_trigger <= s_data[14:10]; |
cfg_on_dev_trigger <= s_data[15]; |
cfg_incs <= !s_data[29]; |
cfg_incd <= !s_data[28]; |
cfg_dev_trigger <= i_swb_data[14:10]; |
cfg_on_dev_trigger <= i_swb_data[15]; |
cfg_incs <= ~i_swb_data[29]; |
cfg_incd <= ~i_swb_data[28]; |
end |
2'b01: begin end // This is done elsewhere |
2'b10: cfg_raddr <= s_data[(AW+2-1):2]; |
2'b11: cfg_waddr <= s_data[(AW+2-1):2]; |
2'b01: begin |
cfg_len <= i_swb_data[(AW-1):0]; |
cfg_len_nonzero <= (|i_swb_data[(AW-1):0]); |
end |
2'b10: cfg_raddr <= i_swb_data[(AW-1):0]; |
2'b11: cfg_waddr <= i_swb_data[(AW-1):0]; |
endcase |
end end |
`DMA_WAIT: begin |
o_mwb_addr <= cfg_raddr; |
nracks <= 0; |
nwacks <= 0; |
nwritten <= 0; |
nread <= 0; |
if (abort) |
dma_state <= `DMA_IDLE; |
else if (user_halt) |
264,10 → 231,13
dma_state <= `DMA_READ_REQ; |
end |
`DMA_READ_REQ: begin |
if (!i_mwb_stall) |
nwritten <= 0; |
|
if (~i_mwb_stall) |
begin |
// Number of read acknowledgements needed |
if ((last_read_request)||(user_halt)) |
nracks <= nracks+1; |
if (last_read_request) |
//((nracks == {1'b0, cfg_blocklen_sub_one})||(bus_nracks == cfg_len-1)) |
// Wishbone interruptus |
dma_state <= `DMA_READ_ACK; |
276,21 → 246,33
+ {{(AW-1){1'b0}},1'b1}; |
end |
|
if ((i_mwb_err)||(abort)) |
if (user_halt) |
dma_state <= `DMA_READ_ACK; |
if (i_mwb_err) |
begin |
cfg_len <= 0; |
dma_state <= `DMA_IDLE; |
else if (i_mwb_ack) |
end |
|
if (abort) |
dma_state <= `DMA_IDLE; |
if (i_mwb_ack) |
begin |
nread <= nread+1; |
if (cfg_incs) |
cfg_raddr <= cfg_raddr |
+ {{(AW-1){1'b0}},1'b1}; |
if (last_read_ack) |
dma_state <= `DMA_PRE_WRITE; |
end end |
`DMA_READ_ACK: begin |
if ((i_mwb_err)||(abort)) |
nwritten <= 0; |
|
if (i_mwb_err) |
begin |
cfg_len <= 0; |
dma_state <= `DMA_IDLE; |
else if (i_mwb_ack) |
end else if (i_mwb_ack) |
begin |
nread <= nread+1; |
if (last_read_ack) // (nread+1 == nracks) |
dma_state <= `DMA_PRE_WRITE; |
if (user_halt) |
299,6 → 281,8
cfg_raddr <= cfg_raddr |
+ {{(AW-1){1'b0}},1'b1}; |
end |
if (abort) |
dma_state <= `DMA_IDLE; |
end |
`DMA_PRE_WRITE: begin |
o_mwb_addr <= cfg_waddr; |
305,8 → 289,9
dma_state <= (abort)?`DMA_IDLE:`DMA_WRITE_REQ; |
end |
`DMA_WRITE_REQ: begin |
if (!i_mwb_stall) |
if (~i_mwb_stall) |
begin |
nwritten <= nwritten+1; |
if (last_write_request) // (nwritten == nread-1) |
// Wishbone interruptus |
dma_state <= `DMA_WRITE_ACK; |
319,20 → 304,39
end |
end |
|
if ((i_mwb_err)||(abort)) |
if (i_mwb_err) |
begin |
cfg_len <= 0; |
dma_state <= `DMA_IDLE; |
else if ((i_mwb_ack)&&(last_write_ack)) |
dma_state <= (cfg_len <= 1)?`DMA_IDLE:`DMA_WAIT; |
else if (!cfg_len_nonzero) |
end |
if (i_mwb_ack) |
begin |
nwacks <= nwacks+1; |
cfg_len <= cfg_len +{(AW){1'b1}}; // -1 |
end |
if (user_halt) |
dma_state <= `DMA_WRITE_ACK; |
if (abort) |
dma_state <= `DMA_IDLE; |
end |
`DMA_WRITE_ACK: begin |
if ((i_mwb_err)||(abort)) |
if (i_mwb_err) |
begin |
cfg_len <= 0; |
nread <= 0; |
dma_state <= `DMA_IDLE; |
else if ((i_mwb_ack)&&(last_write_ack)) |
// (nwacks+1 == nwritten) |
dma_state <= (cfg_len <= 1)?`DMA_IDLE:`DMA_WAIT; |
else if (!cfg_len_nonzero) |
end else if (i_mwb_ack) |
begin |
nwacks <= nwacks+1; |
cfg_len <= cfg_len +{(AW){1'b1}};//cfg_len -= 1; |
if (last_write_ack) // (nwacks+1 == nwritten) |
begin |
nread <= 0; |
dma_state <= (cfg_len == 1)?`DMA_IDLE:`DMA_WAIT; |
end |
end |
|
if (abort) |
dma_state <= `DMA_IDLE; |
end |
default: |
341,150 → 345,71
|
initial o_interrupt = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
o_interrupt <= 1'b0; |
else |
o_interrupt <= ((dma_state == `DMA_WRITE_ACK)&&(i_mwb_ack) |
&&(last_write_ack) |
&&(cfg_len == {{(AW-1){1'b0}},1'b1})) |
||((dma_state != `DMA_IDLE)&&(i_mwb_err)); |
|
|
initial cfg_len = 0; |
initial cfg_len_nonzero = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
begin |
cfg_len <= 0; |
cfg_len_nonzero <= 1'b0; |
end else if ((dma_state == `DMA_IDLE) |
&&(s_stb)&&(s_we)&&(s_addr == 2'b01)) |
begin |
cfg_len <= s_data[(AW-1):0]; |
cfg_len_nonzero <= (|s_data[(AW-1):0]); |
end else if ((o_mwb_cyc)&&(o_mwb_we)&&(i_mwb_ack)) |
begin |
cfg_len <= cfg_len - 1'b1; |
cfg_len_nonzero <= (cfg_len > 1); |
end |
|
initial nracks = 0; |
always @(posedge i_clk) |
if (i_reset) |
nracks <= 0; |
else if ((dma_state == `DMA_IDLE)||(dma_state == `DMA_WAIT)) |
nracks <= 0; |
else if ((o_mwb_stb)&&(!o_mwb_we)&&(!i_mwb_stall)) |
nracks <= nracks + 1'b1; |
|
initial nread = 0; |
always @(posedge i_clk) |
if (i_reset) |
nread <= 0; |
else if ((dma_state == `DMA_IDLE)||(dma_state == `DMA_WAIT)) |
nread <= 0; |
else if ((!o_mwb_we)&&(i_mwb_ack)) |
nread <= nread + 1'b1; |
|
initial nwritten = 0; |
always @(posedge i_clk) |
if (i_reset) |
nwritten <= 0; |
else if ((!o_mwb_cyc)||(!o_mwb_we)) |
nwritten <= 0; |
else if ((o_mwb_stb)&&(!i_mwb_stall)) |
nwritten <= nwritten + 1'b1; |
|
initial nwacks = 0; |
always @(posedge i_clk) |
if (i_reset) |
nwacks <= 0; |
else if ((!o_mwb_cyc)||(!o_mwb_we)) |
nwacks <= 0; |
else if (i_mwb_ack) |
nwacks <= nwacks + 1'b1; |
|
initial cfg_err = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
cfg_err <= 1'b0; |
else if (dma_state == `DMA_IDLE) |
begin |
if ((s_stb)&&(s_we)&&(s_addr==2'b00)) |
cfg_err <= 1'b0; |
end else if (((i_mwb_err)&&(o_mwb_cyc))||(abort)) |
cfg_err <= 1'b1; |
if (dma_state == `DMA_IDLE) |
begin |
if ((i_swb_stb)&&(i_swb_we)&&(i_swb_addr==2'b00)) |
cfg_err <= 1'b0; |
end else if (((i_mwb_err)&&(o_mwb_cyc))||(abort)) |
cfg_err <= 1'b1; |
|
initial last_read_request = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
last_read_request <= 1'b0; |
else if ((dma_state == `DMA_WAIT)||(dma_state == `DMA_READ_REQ)) |
begin |
if ((!i_mwb_stall)&&(dma_state == `DMA_READ_REQ)) |
if ((dma_state == `DMA_WAIT)||(dma_state == `DMA_READ_REQ)) |
begin |
last_read_request <= |
(nracks + 1 == { 1'b0, cfg_blocklen_sub_one}) |
||(bus_nracks == cfg_len-2); |
if ((~i_mwb_stall)&&(dma_state == `DMA_READ_REQ)) |
begin |
last_read_request <= |
(nracks + 1 == { 1'b0, cfg_blocklen_sub_one}) |
||(bus_nracks == cfg_len-2); |
end else |
last_read_request <= |
(nracks== { 1'b0, cfg_blocklen_sub_one}) |
||(bus_nracks == cfg_len-1); |
end else |
last_read_request <= |
(nracks== { 1'b0, cfg_blocklen_sub_one}) |
||(bus_nracks == cfg_len-1); |
end else |
last_read_request <= 1'b0; |
last_read_request <= 1'b0; |
|
|
wire [(LGMEMLEN):0] next_nread; |
assign next_nread = nread + 1'b1; |
|
initial last_read_ack = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
last_read_ack <= 0; |
else if (dma_state == `DMA_READ_REQ) |
begin |
if ((i_mwb_ack)&&((!o_mwb_stb)||(i_mwb_stall))) |
last_read_ack <= (next_nread == { 1'b0, cfg_blocklen_sub_one }); |
else |
last_read_ack <= (nread == { 1'b0, cfg_blocklen_sub_one }); |
end else if (dma_state == `DMA_READ_ACK) |
begin |
if ((i_mwb_ack)&&((!o_mwb_stb)||(i_mwb_stall))) |
last_read_ack <= (nread+2 == nracks); |
else |
last_read_ack <= (next_nread == nracks); |
end else |
last_read_ack <= 1'b0; |
if ((dma_state == `DMA_READ_REQ)||(dma_state == `DMA_READ_ACK)) |
begin |
if ((i_mwb_ack)&&((~o_mwb_stb)||(i_mwb_stall))) |
last_read_ack <= (nread+2 == nracks); |
else |
last_read_ack <= (nread+1 == nracks); |
end else |
last_read_ack <= 1'b0; |
|
initial last_write_request = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
last_write_request <= 1'b0; |
else if (dma_state == `DMA_PRE_WRITE) |
last_write_request <= (nread <= 1); |
else if (dma_state == `DMA_WRITE_REQ) |
begin |
if (i_mwb_stall) |
last_write_request <= (nwritten >= nread-1); |
else |
last_write_request <= (nwritten >= nread-2); |
end else |
last_write_request <= 1'b0; |
if (dma_state == `DMA_PRE_WRITE) |
last_write_request <= (nread <= 1); |
else if (dma_state == `DMA_WRITE_REQ) |
begin |
if (i_mwb_stall) |
last_write_request <= (nwritten >= nread-1); |
else |
last_write_request <= (nwritten >= nread-2); |
end else |
last_write_request <= 1'b0; |
|
initial last_write_ack = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
last_write_ack <= 1'b0; |
else if((dma_state == `DMA_WRITE_REQ)||(dma_state == `DMA_WRITE_ACK)) |
begin |
if ((i_mwb_ack)&&((!o_mwb_stb)||(i_mwb_stall))) |
last_write_ack <= (nwacks+2 == nwritten); |
else |
last_write_ack <= (nwacks+1 == nwritten); |
end else |
last_write_ack <= 1'b0; |
if((dma_state == `DMA_WRITE_REQ)||(dma_state == `DMA_WRITE_ACK)) |
begin |
if ((i_mwb_ack)&&((~o_mwb_stb)||(i_mwb_stall))) |
last_write_ack <= (nwacks+2 == nwritten); |
else |
last_write_ack <= (nwacks+1 == nwritten); |
end else |
last_write_ack <= 1'b0; |
|
|
assign o_mwb_cyc = (dma_state == `DMA_READ_REQ) |
||(dma_state == `DMA_READ_ACK) |
||(dma_state == `DMA_WRITE_REQ) |
505,41 → 430,32
// want to read from, unless we are idling. So ... the math is touchy. |
// |
reg [(LGMEMLEN-1):0] rdaddr; |
|
initial rdaddr = 0; |
always @(posedge i_clk) |
if (i_reset) |
rdaddr <= 0; |
else if((dma_state == `DMA_IDLE)||(dma_state == `DMA_WAIT) |
||(dma_state == `DMA_WRITE_ACK)) |
rdaddr <= 0; |
else if ((dma_state == `DMA_PRE_WRITE) |
||((dma_state==`DMA_WRITE_REQ)&&(!i_mwb_stall))) |
rdaddr <= rdaddr + {{(LGMEMLEN-1){1'b0}},1'b1}; |
|
if((dma_state == `DMA_IDLE)||(dma_state == `DMA_WAIT) |
||(dma_state == `DMA_WRITE_ACK)) |
rdaddr <= 0; |
else if ((dma_state == `DMA_PRE_WRITE) |
||((dma_state==`DMA_WRITE_REQ)&&(~i_mwb_stall))) |
rdaddr <= rdaddr + {{(LGMEMLEN-1){1'b0}},1'b1}; |
always @(posedge i_clk) |
if (i_reset) |
o_mwb_data <= 0; |
else if ((dma_state != `DMA_WRITE_REQ)||(!i_mwb_stall)) |
o_mwb_data <= dma_mem[rdaddr]; |
if ((dma_state != `DMA_WRITE_REQ)||(~i_mwb_stall)) |
o_mwb_data <= dma_mem[rdaddr]; |
always @(posedge i_clk) |
if((dma_state == `DMA_READ_REQ)||(dma_state == `DMA_READ_ACK)) |
dma_mem[nread[(LGMEMLEN-1):0]] <= i_mwb_data; |
|
always @(posedge i_clk) |
if (i_reset) |
o_swb_data <= 0; |
else casez(s_addr) |
casez(i_swb_addr) |
2'b00: o_swb_data <= { (dma_state != `DMA_IDLE), cfg_err, |
!cfg_incs, !cfg_incd, |
~cfg_incs, ~cfg_incd, |
1'b0, nread, |
cfg_on_dev_trigger, cfg_dev_trigger, |
cfg_blocklen_sub_one |
}; |
2'b01: o_swb_data <= { {(DW-AW){1'b0}}, cfg_len }; |
2'b10: o_swb_data <= { {(DW-2-AW){1'b0}}, cfg_raddr, 2'b00 }; |
2'b11: o_swb_data <= { {(DW-2-AW){1'b0}}, cfg_waddr, 2'b00 }; |
endcase |
2'b10: o_swb_data <= { {(DW-AW){1'b0}}, cfg_raddr}; |
2'b11: o_swb_data <= { {(DW-AW){1'b0}}, cfg_waddr}; |
endcase |
|
// This causes us to wait a minimum of two clocks before starting: One |
// to go into the wait state, and then one while in the wait state to |
546,291 → 462,30
// develop the trigger. |
initial trigger = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
trigger <= 1'b0; |
else |
trigger <= (dma_state == `DMA_WAIT) |
&&((!cfg_on_dev_trigger) |
&&((~cfg_on_dev_trigger) |
||(i_dev_ints[cfg_dev_trigger])); |
|
// Ack any access. We'll quietly ignore any access where we are busy, |
// but ack it anyway. In other words, before writing to the device, |
// double check that it isn't busy, and then write. |
initial o_swb_ack = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
o_swb_ack <= 1'b0; |
else if (!i_swb_cyc) |
o_swb_ack <= 1'b0; |
else |
o_swb_ack <= (s_stb); |
o_swb_ack <= (i_swb_stb); |
|
assign o_swb_stall = 1'b0; |
|
initial abort = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
abort <= 1'b0; |
else if (dma_state == `DMA_IDLE) |
abort <= 1'b0; |
else |
abort <= ((s_stb)&&(s_we) |
&&(s_addr == 2'b00) |
&&(s_data == 32'hffed0000)); |
abort <= (i_rst)||((i_swb_stb)&&(i_swb_we) |
&&(i_swb_addr == 2'b00) |
&&(i_swb_data == 32'hffed0000)); |
|
initial user_halt = 1'b0; |
always @(posedge i_clk) |
user_halt <= ((user_halt)&&(dma_state != `DMA_IDLE)) |
||((s_stb)&&(s_we)&&(dma_state != `DMA_IDLE) |
&&(s_addr == 2'b00) |
&&(s_data == 32'hafed0000)); |
||((i_swb_stb)&&(i_swb_we)&&(dma_state != `DMA_IDLE) |
&&(i_swb_addr == 2'b00) |
&&(i_swb_data == 32'hafed0000)); |
|
endmodule |
|
// Make verilator happy |
// verilator lint_off UNUSED |
wire unused; |
assign unused = s_cyc; |
// verilator lint_on UNUSED |
`ifdef FORMAL |
reg f_past_valid; |
initial f_past_valid = 1'b0; |
always @(posedge i_clk) |
f_past_valid <= 1'b1; |
|
always @(*) |
if (!f_past_valid) |
assume(i_reset); |
|
always @(posedge i_clk) |
if ((!f_past_valid)||($past(i_reset))) |
assert(dma_state == `DMA_IDLE); |
|
parameter F_SLV_LGDEPTH = 3; |
parameter F_MSTR_LGDEPTH = LGMEMLEN+1; |
|
wire [F_SLV_LGDEPTH-1:0] f_swb_nreqs, f_swb_nacks, f_swb_outstanding; |
wire [F_MSTR_LGDEPTH-1:0] f_mwb_nreqs, f_mwb_nacks, f_mwb_outstanding; |
|
fwb_slave #( |
.AW(2), .DW(32), .F_MAX_STALL(0), .F_MAX_ACK_DELAY(2), |
.F_LGDEPTH(F_SLV_LGDEPTH) |
) control_port(i_clk, i_reset, |
i_swb_cyc, i_swb_stb, i_swb_we, i_swb_addr, i_swb_data,4'b1111, |
o_swb_ack, o_swb_stall, o_swb_data, 1'b0, |
f_swb_nreqs, f_swb_nacks, f_swb_outstanding); |
always @(*) |
assert(o_swb_stall == 0); |
`ifdef DELAY_ACCESS |
always @(*) |
if ((!i_reset)&&(i_swb_cyc)) |
begin |
if ((!s_stb)&&(!o_swb_ack)) |
assert(f_swb_outstanding == 0); |
else if ((s_stb)&&(!o_swb_ack)) |
assert(f_swb_outstanding == 1); |
else if ((!s_stb)&&(o_swb_ack)) |
assert(f_swb_outstanding == 1); |
else if ((s_stb)&&(o_swb_ack)) |
assert(f_swb_outstanding == 2); |
end |
`else |
always @(*) |
if ((!i_reset)&&(!o_swb_ack)) |
assert(f_swb_outstanding == 0); |
`endif |
|
fwb_master #(.AW(AW), .DW(32), .F_MAX_STALL(4), .F_MAX_ACK_DELAY(8), |
.F_LGDEPTH(F_MSTR_LGDEPTH), |
.F_OPT_RMW_BUS_OPTION(1'b1), |
.F_OPT_DISCONTINUOUS(1'b0), |
.F_OPT_SOURCE(1'b1) |
) external_bus(i_clk, i_reset, |
o_mwb_cyc, o_mwb_stb, o_mwb_we, o_mwb_addr, o_mwb_data,4'b1111, |
i_mwb_ack, i_mwb_stall, i_mwb_data, i_mwb_err, |
f_mwb_nreqs, f_mwb_nacks, f_mwb_outstanding); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(dma_state == `DMA_IDLE))) |
assert(o_mwb_cyc == 1'b0); |
|
always @(*) |
if ((o_mwb_cyc)&&(!o_mwb_we)) |
begin |
assert(nracks == f_mwb_nreqs); |
assert(nread == f_mwb_nacks); |
end |
|
always @(*) |
if ((o_mwb_cyc)&&(o_mwb_we)) |
begin |
assert(nwacks == f_mwb_nacks); |
assert(nwritten == f_mwb_nreqs); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(abort))&&($past(dma_state != `DMA_IDLE))) |
assert(dma_state == `DMA_IDLE); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_mwb_cyc))&&(o_mwb_cyc)&&( |
(( !cfg_incs)&&(!o_mwb_we)) |
||((!cfg_incd)&&( o_mwb_we)))) |
begin |
assert(o_mwb_addr == $past(o_mwb_addr)); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_mwb_cyc))&&( |
((!cfg_incs)||($past(o_mwb_we))||(!$past(i_mwb_ack))))) |
assert(cfg_raddr == $past(cfg_raddr)); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(dma_state == `DMA_WRITE_REQ)) |
assert(cfg_waddr == o_mwb_addr); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset)) |
&&($past(o_mwb_stb))&&(!$past(i_mwb_stall))) |
begin |
assert( ((!cfg_incs)&&(!$past(o_mwb_we))) |
||((!cfg_incd)&&( $past(o_mwb_we))) |
||(o_mwb_addr==$past(o_mwb_addr)+1'b1)); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(o_mwb_cyc))&&(o_mwb_cyc)) |
begin |
if (o_mwb_we) |
assert(o_mwb_addr == cfg_waddr); |
else |
assert(o_mwb_addr == cfg_raddr); |
end |
|
always @(*) |
assert(cfg_len_nonzero == (cfg_len != 0)); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_reset))) |
begin |
assert(cfg_len == 0); |
assert(!cfg_len_nonzero); |
end else if ((f_past_valid)&&($past(o_mwb_cyc))) |
begin |
if (($past(i_mwb_ack))&&($past(o_mwb_we))) |
assert(cfg_len == $past(cfg_len)-1'b1); |
else |
assert(cfg_len == $past(cfg_len)); |
end else if ((f_past_valid)&&(($past(dma_state) != `DMA_IDLE) |
||(!$past(s_stb))||(!$past(s_we)) |
||($past(s_addr)!=2'b01))) |
assert(cfg_len == $past(cfg_len)); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_mwb_cyc))&&($past(cfg_len == 0)) |
&&(!$past(user_halt))) |
begin |
assert(cfg_len == 0); |
assert((dma_state != $past(dma_state))||(!o_mwb_cyc)); |
end |
|
always @(posedge i_clk) |
if (cfg_len == 0) |
assert(!o_mwb_stb); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(dma_state) != `DMA_IDLE)) |
begin |
assert(cfg_incs == $past(cfg_incs)); |
assert(cfg_incd == $past(cfg_incd)); |
assert(cfg_blocklen_sub_one == $past(cfg_blocklen_sub_one)); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(dma_state) == `DMA_IDLE)) |
assert(cfg_len_nonzero == (cfg_len != 0)); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(o_mwb_cyc))||(!$past(o_mwb_we))) |
assert((nwritten == 0)&&(nwacks == 0)); |
always @(posedge i_clk) |
if ((o_mwb_cyc)&&(!o_mwb_we)) |
assert(bus_nracks <= cfg_len); |
always @(posedge i_clk) |
if ((o_mwb_cyc)&&(!o_mwb_we)) |
assert(nread <= nracks); |
always @(posedge i_clk) |
if ((o_mwb_cyc)&&(o_mwb_we)) |
assert(nwritten-nwacks |
+((o_mwb_stb)? 1'b1:1'b0) |
- f_mwb_outstanding |
// -((i_mwb_ack)? 1'b1:1'b0) |
<= cfg_len); |
always @(*) |
assert(f_mwb_outstanding |
+ ((o_mwb_stb)? 1'b1:1'b0) <= cfg_len); |
|
wire [LGMEMLEN:0] f_cfg_blocklen; |
assign f_cfg_blocklen = { 1'b0, cfg_blocklen_sub_one} + 1'b1; |
|
always @(*) |
if (dma_state == `DMA_WAIT) |
assert(cfg_len > 0); |
|
always @(*) |
if ((o_mwb_stb)&&(o_mwb_we)) |
assert(nread == nracks); |
|
always @(*) |
if (o_mwb_stb) |
assert(nwritten <= cfg_blocklen_sub_one); |
always @(posedge i_clk) |
assert(nwritten <= f_cfg_blocklen); |
|
always @(*) |
if ((o_mwb_stb)&&(!o_mwb_we)) |
assert(nracks < f_cfg_blocklen); |
else |
assert(nracks <= f_cfg_blocklen); |
always @(*) |
if ((o_mwb_cyc)&&(i_mwb_ack)&&(!o_mwb_we)) |
assert(nread < f_cfg_blocklen); |
always @(*) |
assert(nread <= nracks); |
|
always @(*) |
if ((o_mwb_cyc)&&(o_mwb_we)&&(!user_halt)) |
assert(nread == nracks); |
|
always @(*) |
if ((o_mwb_cyc)&&(o_mwb_we)) |
assert(nwritten >= nwacks); |
|
always @(*) |
if (dma_state == `DMA_WRITE_REQ) |
assert(last_write_request == (nwritten == nread-1)); |
|
always @(*) |
assert(nwritten >= nwacks); |
|
always @(*) |
assert(nread >= nwritten); |
|
always @(*) |
assert(nracks >= nread); |
|
wire [LGMEMLEN:0] f_npending; |
assign f_npending = nread-nwacks; |
always @(*) |
if (dma_state != `DMA_IDLE) |
assert({ {(AW-LGMEMLEN-1){1'b0}}, f_npending} <= cfg_len); |
|
always @(posedge i_clk) |
begin |
assert(cfg_len_nonzero == (cfg_len != 0)); |
if ((f_past_valid)&&($past(dma_state != `DMA_IDLE))&&($past(cfg_len == 0))) |
assert(cfg_len == 0); |
end |
|
|
`endif |
endmodule |
/peripherals/wbwatchdog.v
6,24 → 6,32
// |
// Purpose: A Zip timer, redesigned to be a bus watchdog |
// |
// This is basically a timer, but there are some unique features to it. |
// This is a **really** stripped down Zip Timer. All options for external |
// control have been removed. This timer may be reset, and ... that's |
// about it. The goal is that this stripped down timer be used as a bus |
// watchdog element. Even at that, it's not really fully featured. The |
// rest of the important features can be found in the zipsystem module. |
// |
// 1. There is no way to "write" the timeout to this watchdog. It is |
// fixed with an input (that is assumed to be constant) |
// 2. The counter returns to i_timer and the interrupt is cleared on any |
// reset. |
// 3. Between resets, the counter counts down to zero. Once (and if) it |
// hits zero, it will remain at zero until reset. |
// 4. Any time the counter is at zero, and until the reset that resets |
// the counter, the output interrupt will be set. |
// As a historical note, the wishbone watchdog timer began as a normal |
// timer, with some fixed inputs. This makes sense, if you think about it: |
// if the goal is to interrupt a stalled wishbone transaction by inserting |
// a bus error, then you can't use the bus to set it up or configure it |
// simply because the bus in question is ... well, unreliable. You're |
// trying to make it reliable. |
// |
// The problem with using the ziptimer in a stripped down implementation |
// was that the fixed inputs caused the synthesis tool to complain about |
// the use of registers values would never change. This solves that |
// problem by explicitly removing the cruft that would otherwise |
// just create synthesis warnings and errors. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015,2017-2019, Gisselquist Technology, LLC |
// Copyright (C) 2015,2017, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
47,84 → 55,28
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
// |
module wbwatchdog(i_clk, i_reset, i_timeout, o_int); |
module wbwatchdog(i_clk, i_rst, i_ce, i_timeout, o_int); |
parameter BW = 32; |
input wire i_clk, i_reset; |
input i_clk, i_rst, i_ce; |
// Inputs (these were at one time wishbone controlled ...) |
input wire [(BW-1):0] i_timeout; |
input [(BW-1):0] i_timeout; |
// Interrupt line |
output reg o_int; |
|
reg [(BW-1):0] r_value; |
initial r_value = {(BW){1'b1}}; |
initial r_value = 0; |
always @(posedge i_clk) |
if (i_reset) |
if (i_rst) |
r_value <= i_timeout[(BW-1):0]; |
else if (!o_int) |
else if ((i_ce)&&(~o_int)) |
r_value <= r_value + {(BW){1'b1}}; // r_value - 1; |
|
// Set the interrupt on our last tick. |
initial o_int = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
if ((i_rst)||(~i_ce)) |
o_int <= 1'b0; |
else if (!o_int) |
else |
o_int <= (r_value == { {(BW-1){1'b0}}, 1'b1 }); |
|
`ifdef FORMAL |
reg f_past_valid; |
|
initial f_past_valid = 1'b0; |
always @(posedge f_past_valid) |
f_past_valid <= 1'b1; |
|
/////////////////////////////////////////////// |
// |
// |
// Assumptions about our inputs |
// |
// |
/////////////////////////////////////////////// |
always @(*) |
assume(i_timeout > 1); |
always @(posedge i_clk) |
if (f_past_valid) |
assume(i_timeout == $past(i_timeout)); |
|
// |
// |
/////////////////////////////////////////////// |
// |
// |
// Assertions about our internal state and our outputs |
// |
// |
/////////////////////////////////////////////// |
// |
// |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_int))&&(!$past(i_reset))) |
assert(o_int); |
|
always @(*) |
assert(o_int == (r_value == 0)); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&(!$past(o_int))) |
begin |
assert(r_value == $past(r_value)-1'b1); |
end |
|
always @(posedge i_clk) |
if ((!f_past_valid)||($past(i_reset))) |
begin |
if (!f_past_valid) |
assert(r_value == {(BW){1'b1}}); |
else // if ($past(i_reset)) |
assert(r_value == $past(i_timeout)); |
assert(!o_int); |
end |
`endif |
endmodule |
/peripherals/zipcounter.v
27,7 → 27,7
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2019, Gisselquist Technology, LLC |
// Copyright (C) 2015-2017, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
51,20 → 51,15
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
// |
module zipcounter(i_clk, i_reset, i_event, |
module zipcounter(i_clk, i_ce, |
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_data, |
o_wb_ack, o_wb_stall, o_wb_data, |
o_int); |
parameter BW = 32; |
// |
localparam F_LGDEPTH = 2; |
// |
input wire i_clk, i_reset, i_event; |
input i_clk, i_ce; |
// Wishbone inputs |
input wire i_wb_cyc, i_wb_stb, i_wb_we; |
input wire [(BW-1):0] i_wb_data; |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [(BW-1):0] i_wb_data; |
// Wishbone outputs |
output reg o_wb_ack; |
output wire o_wb_stall; |
75,11 → 70,9
initial o_int = 0; |
initial o_wb_data = 32'h00; |
always @(posedge i_clk) |
if (i_reset) |
{ o_int, o_wb_data } <= 0; |
else if ((i_wb_stb)&&(i_wb_we)) |
if ((i_wb_stb)&&(i_wb_we)) |
{ o_int, o_wb_data } <= { 1'b0, i_wb_data }; |
else if (i_event) |
else if (i_ce) |
{ o_int, o_wb_data } <= o_wb_data+{{(BW-1){1'b0}},1'b1}; |
else |
o_int <= 1'b0; |
86,124 → 79,6
|
initial o_wb_ack = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
o_wb_ack <= 1'b0; |
else |
o_wb_ack <= i_wb_stb; |
o_wb_ack <= (i_wb_stb); |
assign o_wb_stall = 1'b0; |
|
|
// Make verilator happy |
// verilator lint_off UNUSED |
wire unused; |
assign unused = i_wb_cyc; |
// verilator lint_on UNUSED |
|
`ifdef FORMAL |
reg f_past_valid; |
initial f_past_valid = 1'b0; |
always @(posedge i_clk) |
f_past_valid <= 1'b1; |
|
always @(*) |
if (!f_past_valid) |
assume(i_reset); |
|
//////////////////////////////////////////////// |
// |
// |
// Assumptions about our inputs |
// |
// |
//////////////////////////////////////////////// |
// |
|
//////////////////////////////////////////////// |
// |
// |
// Bus interface properties |
// |
// |
//////////////////////////////////////////////// |
// |
|
// We never stall the bus |
always @(*) |
assert(!o_wb_stall); |
|
// We always ack every transaction on the following clock |
always @(posedge i_clk) |
assert(o_wb_ack == ((f_past_valid)&&(!$past(i_reset)) |
&&($past(i_wb_stb)))); |
|
wire [(F_LGDEPTH-1):0] f_nreqs, f_nacks, f_outstanding; |
|
fwb_slave #( .AW(1), .F_MAX_STALL(0), |
.F_MAX_ACK_DELAY(1), .F_LGDEPTH(F_LGDEPTH) |
) fwbi(i_clk, i_reset, |
i_wb_cyc, i_wb_stb, i_wb_we, 1'b0, i_wb_data, 4'hf, |
o_wb_ack, o_wb_stall, o_wb_data, 1'b0, |
f_nreqs, f_nacks, f_outstanding); |
|
always @(*) |
if ((o_wb_ack)&&(i_wb_cyc)) |
assert(f_outstanding==1); |
else |
assert(f_outstanding == 0); |
|
//////////////////////////////////////////////// |
// |
// |
// Assumptions about our outputs |
// |
// |
//////////////////////////////////////////////// |
// |
|
// Drop the interrupt line and reset the counter on any reset |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_reset))) |
assert((!o_int)&&(o_wb_data == 0)); |
|
// Clear the interrupt and set the counter on any write (other than |
// during a reset) |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset)) |
&&($past(i_wb_stb))&&($past(i_wb_we))) |
assert((!o_int)&&(o_wb_data == $past(i_wb_data))); |
|
// Normal logic of the routine itself |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&(!$past(i_wb_stb))) |
begin |
if (!$past(i_event)) |
begin |
// If the CE line wasn't set on the last clock, then the |
// counter must not change, and the interrupt line must |
// be low. |
assert(o_wb_data == $past(o_wb_data)); |
assert(!o_int); |
end else // if ($past(i_event)) |
begin |
// Otherwise, if the CE line was high on the last clock, |
// then our counter should have incremented. |
assert(o_wb_data == $past(o_wb_data) + 1'b1); |
|
// Likewise, if the counter rolled over, then the |
// output interrupt, o_int, should be true. |
if ($past(o_wb_data)=={(BW){1'b1}}) |
assert(o_int); |
else |
// In all other circumstances it should be clear |
assert(!o_int); |
end |
end |
|
// |
// The output interrupt should never be true two clocks in a row |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_int))) |
assert(!o_int); |
|
`endif |
endmodule |
/peripherals/zipjiffies.v
45,7 → 45,7
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2019, Gisselquist Technology, LLC |
// Copyright (C) 2015-2017, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
69,17 → 69,15
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
// |
module zipjiffies(i_clk, i_reset, i_ce, |
module zipjiffies(i_clk, i_ce, |
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_data, |
o_wb_ack, o_wb_stall, o_wb_data, |
o_int); |
parameter BW = 32; |
input wire i_clk, i_reset, i_ce; |
input i_clk, i_ce; |
// Wishbone inputs |
input wire i_wb_cyc, i_wb_stb, i_wb_we; |
input wire [(BW-1):0] i_wb_data; |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [(BW-1):0] i_wb_data; |
// Wishbone outputs |
output reg o_wb_ack; |
output wire o_wb_stall; |
96,11 → 94,8
// together, one clock at a time. |
// |
reg [(BW-1):0] r_counter; |
initial r_counter = 0; |
always @(posedge i_clk) |
if (i_reset) |
r_counter <= 0; |
else if (i_ce) |
if (i_ce) |
r_counter <= r_counter+1; |
|
// |
115,13 → 110,9
|
initial new_set = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
begin |
new_set <= 1'b0; |
new_when <= 0; |
end else begin |
// Delay WB commands (writes) by a clock to simplify our logic |
new_set <= ((i_wb_stb)&&(i_wb_we)); |
// Delay things by a clock to simplify our logic |
new_set <= ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_we)); |
// new_when is a don't care when new_set = 0, so don't worry |
// about setting it at all times. |
new_when<= i_wb_data; |
130,11 → 121,7
initial o_int = 1'b0; |
initial int_set = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
begin |
o_int <= 0; |
int_set <= 0; |
end else begin |
o_int <= 1'b0; |
if ((i_ce)&&(int_set)&&(r_counter == int_when)) |
// Interrupts are self-clearing |
146,151 → 133,18
int_set <= 1'b1; |
else if ((i_ce)&&(r_counter == int_when)) |
int_set <= 1'b0; |
|
if ((new_set)&&(till_wb > 0)&&((till_wb<till_when)||(~int_set))) |
int_when <= new_when; |
end |
|
always @(posedge i_clk) |
if ((new_set)&&(till_wb > 0)&&((till_wb<till_when)||(!int_set))) |
int_when <= new_when; |
|
// |
// Acknowledge any wishbone accesses -- everything we did took only |
// one clock anyway. |
// |
initial o_wb_ack = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
o_wb_ack <= 1'b0; |
else |
o_wb_ack <= i_wb_stb; |
o_wb_ack <= (i_wb_cyc)&&(i_wb_stb); |
|
assign o_wb_data = r_counter; |
assign o_wb_stall = 1'b0; |
|
// Make verilator happy |
// verilator lint_off UNUSED |
wire unused; |
assign unused = i_wb_cyc; |
// verilator lint_on UNUSED |
`ifdef FORMAL |
reg f_past_valid; |
initial f_past_valid = 1'b0; |
always @(posedge i_clk) |
f_past_valid <= 1'b1; |
|
//////////////////////////////////////////////// |
// |
// |
// Assumptions about our inputs |
// |
// |
//////////////////////////////////////////////// |
// |
// Some basic WB assumtions |
|
// We will not start out in a wishbone cycle |
initial assume(!i_wb_cyc); |
|
// Following any reset the cycle line will be low |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_reset))) |
assume(!i_wb_cyc); |
|
// Anytime the stb is high, the cycle line must also be high |
always @(posedge i_clk) |
assume((!i_wb_stb)||(i_wb_cyc)); |
|
|
//////////////////////////////////////////////// |
// |
// |
// Assumptions about our bus outputs |
// |
// |
//////////////////////////////////////////////// |
// |
|
// We never stall the bus |
always @(*) |
assert(!o_wb_stall); |
// We always ack every transaction on the following clock |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wb_stb))) |
assert(o_wb_ack); |
else |
assert(!o_wb_ack); |
|
|
//////////////////////////////////////////////// |
// |
// |
// Assumptions about our internal state and our outputs |
// |
// |
//////////////////////////////////////////////// |
// |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_reset))) |
begin |
assert(!o_wb_ack); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wb_stb)) |
&&($past(i_wb_we))) |
assert(new_when == $past(i_wb_data)); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wb_stb)) |
&&($past(i_wb_we))) |
assert(new_set); |
else |
assert(!new_set); |
|
// |
// |
// |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_reset))) |
assert(!o_int); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_reset))) |
begin |
assert(!int_set); |
assert(!new_set); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(new_set)) |
&&(!$past(till_wb[BW-1])) |
&&($past(till_wb) > 0)) |
assert(int_set); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_ce)) |
&&($past(r_counter)==$past(int_when))) |
begin |
assert((o_int)||(!$past(int_set))); |
assert((!int_set)||($past(new_set))); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&(!$past(new_set))&&(!$past(int_set))) |
assert(!int_set); |
|
always @(posedge i_clk) |
if ((!f_past_valid)||($past(i_reset))) |
assert(!o_int); |
else if (($past(new_set))&&($past(till_wb) < 0)) |
assert(o_int); |
|
always @(posedge i_clk) |
if ((f_past_valid)&& |
((!$past(new_set)) |
||($past(till_wb[BW-1])) |
||($past(till_wb == 0)))) |
assert(int_when == $past(int_when)); |
// |
`endif |
endmodule |
/peripherals/zipmmu.v
8,7 → 8,7
// unit, that is configured from one wishbone bus and modifies a |
// separate wishbone bus. Both busses will not be active at the same time. |
// |
// The idea is that the CPU can use one portion of its peripheral |
// The idea is that the CPU can use one portion of its peripheral |
// system memory space to configure the MMU, and another portion of its |
// memory space to access the MMU. Even more, configuring the MMU is to |
// be done when the CPU is in supervisor mode. This means that all |
33,7 → 33,7
// clock cycle. Further, multiple accesses to the same page |
// should not take any longer than the one cycle delay. Accesses |
// to other pages should take a minimum number of clocks. |
// Accesses from one page to the next, such as from one page to |
// Accesses from one page to the next, such as from one page to |
// the next subsequent one, should cost no delays. |
// |
// 2. One independent control word to set the current context |
48,7 → 48,7
// bus of width (lgpage+17), or a memory space of (2^(lgpage+17)). |
// Under this formula, the number of valid address bits can range |
// from 17 to 32. |
// - Contains 4 RdOnly bits indicating log_2 TLB table size. |
// - Contains 4 RdOnly bits indicating log_2 TLB table size. |
// Size is given by (2^(lgsize)). I'm considering sizes of 6,7&8 |
// - Contains 4 RdOnly bits indicating the log page size, offset by |
// eight. Page sizes are therefore given by (2^(lgpage+8)), and |
69,7 → 69,7
// Supervisor *cannot* have page table entries, since there are no |
// interrupts (page faults) allowed in supervisor context. |
// |
// To be valid, |
// To be valid, |
// Context Size (1..16), NFlags ( 4) < Page Size (8-23 bits) |
// Page size (8-23 bits) > NFlags bits (4) |
// |
78,7 → 78,7
// 3. One status word, which contains the address that failed and some |
// flags: |
// |
// Top Virtual address bits indicate which page ... caused a problem. |
// Top Virtual address bits indicate which page ... caused a problem. |
// These will be the top N bits of the word, where N is the size |
// of the virtual address bits. (Bits are cleared upon any write.) |
// |
87,7 → 87,7
// - 4: Multiple page table matches |
// - 2: Attempt to write a read-only page |
// - 1: Page not found |
// |
// |
// 3. Two words per active page table entry, accessed through two bus |
// addresses. This word contains: |
// |
103,16 → 103,19
// 1-bit Read-only / ~written (user set/read/written) |
// If set, this page will cause a fault on any |
// attempt to write this memory. |
// 1-bit This page may be executed |
// 1-bit Cacheable |
// This is not a hardware page, but a memory page. |
// Therefore, the values within this page may be |
// cached. |
// 1-bit Accessed |
// This an be used to implement a least-recently |
// used measure. The hardware will set this value |
// when the page is accessed. The user can also |
// set or clear this at will. |
// 1-bit Cacheable |
// This is not a hardware page, but a memory page. |
// Therefore, the values within this page may be |
// cached. |
// 1-bit This context |
// This is a read-only bit, indicating that the |
// context register of this address matches the |
// context register in the control word. |
// |
// (Loaded flag Not necessary, just map the physical page to 0) |
// |
122,7 → 125,7
// |
// 4. Can read/write this word in two parts: |
// |
// (20-bit Virtual )(8-bits lower context)(4-bit flags), and |
// (20-bit Virtual )(8-bits lower context)(4-bit flags), and |
// (20-bit Physical)(8-bits upper context)(4-bit flags) |
// |
// Actual bit lengths will vary as the MMU configuration changes, |
137,10 → 140,10
// bits in the control register. |
// |
// +----+----+-----+----+----+----+----+--+--+--+--+ |
// | | Lower 8b| R| E| C| A| |
// | 20-bit Virtual page ID | Context | O| X| C| C| |
// |(top 20 bits of the addr)| ID | n| E| H| C| |
// | | | W| F| E| S| |
// | | Lower 8b| R| A| C| T| |
// | 20-bit Virtual page ID | Context | O| C| C| H| |
// |(top 20 bits of the addr)| ID | n| C| H| S| |
// | | | W| S| E| P| |
// +----+----+-----+----+----+----+----+--+--+--+--+ |
// |
// +----+----+-----+----+----+----+----+--+--+--+--+ |
147,7 → 150,7
// | | Upper 8b| R| A| C| T| |
// | 20-bit Physical pg ID | Context | O| C| C| H| |
// |(top 20 bits of the | ID | n| C| H| S| |
// | physical address) | | W| S| E| P| |
// | physical address | | W| S| E| P| |
// +----+----+-----+----+----+----+----+--+--+--+--+ |
// |
// 5. PF Cache--handles words in both physical and virtual |
160,7 → 163,7
// will have long been available, the "Valid" bit will be turned |
// on and associated with the physical mapping. |
// - On any data-write (pf doesn't write), MMU sends [Context,Va,Pa] |
// TLB mapping to the pf-cache. |
// TLB mapping to the pf-cache. |
// - If the write matches any physical PF-cache addresses (???), the |
// pfcache declares that address line invalid, and just plain |
// clears the valid bit for that page. |
186,7 → 189,7
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2016-2019, Gisselquist Technology, LLC |
// Copyright (C) 2016-2017, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
208,20 → 211,9
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
// |
`define ROFLAG 3 // Read-only flag |
`define EXEFLG 2 // No-execute flag (invalid for I-cache) |
`define CHFLAG 1 // Cachable flag |
`define AXFLAG 0 // Accessed flag |
// |
module zipmmu(i_clk, i_reset, i_wbs_cyc_stb, i_wbs_we, i_wbs_addr, |
i_wbs_data, o_wbs_ack, o_wbs_stall, o_wbs_data, |
i_wbm_cyc, i_wbm_stb, i_wbm_we, i_wbm_exe, |
i_wbm_addr, i_wbm_data, i_wbm_sel, i_gie, |
o_cyc, o_stb, o_we, o_addr, o_data, o_sel, |
module zipmmu(i_clk, i_reset, i_ctrl_cyc_stb, i_wbm_cyc, i_wbm_stb, i_wb_we, |
i_wb_addr, i_wb_data, |
o_cyc, o_stb, o_we, o_addr, o_data, |
i_stall, i_ack, i_err, i_data, |
o_rtn_stall, o_rtn_ack, o_rtn_err, |
o_rtn_miss, o_rtn_data, |
228,66 → 220,36
pf_return_stb, pf_return_we, |
pf_return_p, pf_return_v, |
pf_return_cachable); |
parameter // The size of the address bus. Actual addressable |
// size will likely be 2^(ADDRESS_WIDTH+2) octets |
ADDRESS_WIDTH=28, |
// Number of page table entries |
`ifdef FORMAL |
LGTBL=4'h2, |
`else |
LGTBL=4'h6, |
`endif |
// The requested log page size in 8-bit bytes |
PLGPGSZB=20, |
// Number of bits describing context |
`ifdef FORMAL |
PLGCTXT=2; |
`else |
PLGCTXT=16; |
`endif |
parameter [0:0] OPT_DELAY_RETURN = 1'b0; |
parameter ADDRESS_WIDTH=28, LGTBL=6, PLGPGSZ=12, PLGCTXT=16, DW=32; |
localparam // And for our derived parameters (don't set these ...) |
// Width of the data bus is 32-bits. This may be hard |
// to change. |
DW = 32, |
// AW is just shorthand for the name ADDRESS_WIDTH |
AW = ADDRESS_WIDTH, |
// Page sizes must allow for a minimum of one context |
// bit per page, plus four flag bits, hence the minimum |
// number of bits for an address within a page is 5 |
LGPGSZB=(PLGPGSZB < 5)? 5:PLGPGSZB, // in bytes |
LGPGSZW=LGPGSZB-2, // in words |
// The context value for a given page can be split |
// across both virtual and physical words. It cannot |
// have so many bits to it that it takes more bits |
// then are available. |
LGCTXT=((2*LGPGSZB-4)>PLGCTXT)? |
PLGCTXT:(2*LGPGSZB-4), |
LGPGSZ=(PLGPGSZ < 5)? 5:PLGPGSZ, |
// The number of context bits is twice the number of |
// bits left over from DW after removing the LGPGSZ |
// and flags bits. |
LGCTXT=(((DW-LGPGSZ-4)<<1)<PLGCTXT)? |
((DW-LGPGSZ-4)<<1):PLGCTXT, |
// LGLCTX is the number of context bits in the low word |
LGLCTX=(LGCTXT > (LGPGSZB-4))?(LGPGSZB-4):LGCTXT, |
LGLCTX=((LGPGSZ-4)<LGCTXT)?(LGPGSZ-4):LGCTXT, |
// LGHCTX is the number of context bits in the high word |
LGHCTX= (LGCTXT-LGLCTX>0)?(LGCTXT-LGLCTX):0, |
VAW=(DW-LGPGSZB), // Virtual address width, in bytes |
PAW=(AW-LGPGSZW), // Physical address width, in words |
LGHCTX= (LGCTXT-LGLCTX), |
VAW=(DW-LGPGSZ), // Virtual address width |
PAW=(AW-LGPGSZ), // Physical address width |
TBL_BITS = LGTBL, // Bits necessary to addr tbl |
TBL_SIZE=(1<<TBL_BITS);// Number of table entries |
input wire i_clk, i_reset; |
input i_clk, i_reset; |
// |
input wire i_wbs_cyc_stb; |
input wire i_wbs_we; |
input wire [(LGTBL+1):0] i_wbs_addr; |
input wire [(DW-1):0] i_wbs_data; |
output reg o_wbs_ack; |
output wire o_wbs_stall; |
output reg [(DW-1):0] o_wbs_data; |
input i_ctrl_cyc_stb; |
// |
input wire i_wbm_cyc, i_wbm_stb; |
input i_wbm_cyc, i_wbm_stb; |
// |
input wire i_wbm_we, i_wbm_exe; |
input wire [(DW-2-1):0] i_wbm_addr; |
input wire [(DW-1):0] i_wbm_data; |
input wire [(DW/8-1):0] i_wbm_sel; |
input wire i_gie; |
input i_wb_we; |
input [(DW-1):0] i_wb_addr; |
input [(DW-1):0] i_wb_data; |
// |
// Here's where we drive the slave side of the bus |
output reg o_cyc; |
294,18 → 256,17
output wire o_stb, o_we; |
output reg [(AW-1):0] o_addr; |
output reg [(DW-1):0] o_data; |
output reg [(DW/8-1):0] o_sel; |
// and get our return information from driving the slave ... |
input wire i_stall, i_ack, i_err; |
input wire [(DW-1):0] i_data; |
input i_stall, i_ack, i_err; |
input [(DW-1):0] i_data; |
// |
// Here's where we return information on either our slave/control bus |
// or the memory bus we are controlled from. Note that we share these |
// wires ... |
output wire o_rtn_stall; |
output wire o_rtn_ack; |
output reg o_rtn_ack; |
output wire o_rtn_err, o_rtn_miss; |
output wire [(DW-1):0] o_rtn_data; |
output [(DW-1):0] o_rtn_data; |
// Finally, to allow the prefetch to snoop on the MMU conversion ... |
output wire pf_return_stb, // snoop data is valid |
pf_return_we; // snoop data is chnging |
318,67 → 279,60
// |
// |
// |
reg [3:0] tlb_flags [0:(TBL_SIZE-1)]; |
wire [3:0] s_tlb_flags; |
reg [3:1] tlb_flags [0:(TBL_SIZE-1)]; |
reg [(LGCTXT-1):0] tlb_cdata [0:(TBL_SIZE-1)]; |
reg [(VAW-1):0] tlb_vdata [0:(TBL_SIZE-1)]; |
reg [(PAW-1):0] tlb_pdata [0:(TBL_SIZE-1)]; |
reg [(TBL_SIZE-1):0] tlb_valid, tlb_accessed; |
reg [(DW-LGPGSZ-1):0] tlb_vdata [0:(TBL_SIZE-1)]; |
reg [(AW-LGPGSZ-1):0] tlb_pdata [0:(TBL_SIZE-1)]; |
|
wire adr_control, adr_vtable, adr_ptable; |
wire wr_control, wr_vtable, wr_ptable; |
wire adr_control, adr_status, adr_vtable, adr_ptable; |
wire wr_control, wr_status, wr_vtable, wr_ptable; |
wire [(LGTBL-1):0] wr_tlb_addr; |
assign wr_tlb_addr= i_wbs_addr[(LGTBL):1]; // Leave bottom for V/P |
assign adr_control= (i_wbs_cyc_stb)&&(~i_wbs_addr[(LGTBL+1)])&&(~i_wbs_addr[0]); |
assign adr_vtable = (i_wbs_cyc_stb)&&( i_wbs_addr[(LGTBL+1)])&&(~i_wbs_addr[0]); |
assign adr_ptable = (i_wbs_cyc_stb)&&( i_wbs_addr[(LGTBL+1)])&&( i_wbs_addr[0]); |
assign wr_control = (adr_control)&&(i_wbs_we); |
assign wr_vtable = (adr_vtable )&&(i_wbs_we); |
assign wr_ptable = (adr_ptable )&&(i_wbs_we); |
assign wr_tlb_addr= i_wb_addr[(LGTBL):1]; // Leave bottom for V/P |
assign adr_control= (i_ctrl_cyc_stb)&&(~i_wb_addr[(LGTBL+1)])&&(~i_wb_addr[0]); |
assign adr_status = (i_ctrl_cyc_stb)&&(~i_wb_addr[(LGTBL+1)])&&( i_wb_addr[0]); |
assign adr_vtable = (i_ctrl_cyc_stb)&&( i_wb_addr[(LGTBL+1)])&&(~i_wb_addr[0]); |
assign adr_ptable = (i_ctrl_cyc_stb)&&( i_wb_addr[(LGTBL+1)])&&( i_wb_addr[0]); |
assign wr_control = (adr_control)&&(i_wb_we); |
assign wr_status = (adr_status )&&(i_wb_we); |
assign wr_vtable = (adr_vtable )&&(i_wb_we); |
assign wr_ptable = (adr_ptable )&&(i_wb_we); |
|
reg z_context; |
wire kernel_context; |
reg [(LGCTXT-1):0] r_context_word; |
reg setup_ack, z_context, setup_this_page_flag; |
reg [(DW-1):0] setup_data; |
reg [(LGCTXT-1):0] r_context_word, setup_page; |
// |
wire [31:0] w_control_data, w_ptable_reg; |
reg [31:0] w_vtable_reg; |
wire [31:0] w_control_data,w_vtable_reg,w_ptable_reg; |
wire [(LGCTXT-1):0] w_ctable_reg; |
reg [31:0] status_word; |
// |
reg rf_miss, rf_ropage, rf_table_err; |
wire [31:0] control_word; |
wire [3:0] lgaddr_bits, lgtblsz_bits, lgpagesz_bits, |
lgcontext_bits; |
|
reg [(AW-(LGPGSZ)):0] r_mmu_err_vaddr; |
wire [(DW-LGPGSZ):0] w_mmu_err_vaddr; |
// |
reg r_pending, r_we, r_exe, r_valid, |
last_page_valid, last_ro, last_exe; |
reg [(DW-3):0] r_addr; |
reg r_pending, r_we, last_page_valid, last_ro, r_valid; |
reg [(DW-1):0] r_addr; |
reg [(DW-1):0] r_data; |
wire [(VAW-1):0] vpage; |
wire [AW-LGPGSZW-1:0] ppage; |
reg [(DW/8-1):0] r_sel; |
reg [(PAW-1):0] last_ppage; |
reg [(VAW-1):0] last_vpage; |
// |
wire [(TBL_SIZE-1):0] r_tlb_match; |
reg [(LGTBL-1):0] s_tlb_addr, last_tlb; |
reg [(LGTBL-1):0] s_tlb_addr; |
reg s_tlb_miss, s_tlb_hit, s_pending; |
// |
wire ro_flag, exe_flag, simple_miss, ro_miss, exe_miss, table_err, cachable; |
reg pf_stb, pf_cachable; |
reg miss_pending; |
wire ro_flag, simple_miss, ro_miss, table_err, cachable; |
reg p_tlb_miss,p_tlb_err, pf_stb, pf_cachable; |
// |
reg rtn_err; |
|
|
wire this_page_valid, pending_page_valid; |
assign this_page_valid = ((last_page_valid) |
&&(i_wbm_addr[(DW-3):(DW-2-VAW)]==last_vpage) |
&&((!last_ro)||(!i_wbm_we)) |
&&((!last_exe)||(!i_wbm_exe))); |
assign pending_page_valid = ((s_pending)&&(s_tlb_hit) |
&&((!r_we)||(!ro_flag)) |
&&((!r_exe)||(exe_flag))); |
|
////////////////////////////////////////// |
// |
// |
// Step one -- handle the control bus--i_wbs_cyc_stb |
// Step one -- handle the control bus--i_ctrl_cyc_stb |
// |
// |
////////////////////////////////////////// |
386,40 → 340,23
begin |
// Write to the Translation lookaside buffer |
if (wr_vtable) |
tlb_vdata[wr_tlb_addr]<=i_wbs_data[(DW-1):LGPGSZB]; |
tlb_vdata[wr_tlb_addr]<=i_wb_data[(DW-1):LGPGSZ]; |
if (wr_ptable) |
tlb_pdata[wr_tlb_addr]<=i_wbs_data[(AW+1):LGPGSZB]; |
tlb_pdata[wr_tlb_addr]<=i_wb_data[(AW-1):LGPGSZ]; |
// Set the context register for the page |
if ((wr_vtable)||(wr_ptable)) |
tlb_flags[wr_tlb_addr] <= i_wb_data[3:1]; |
// Otherwise, keep track of the accessed bit if we ever access this page |
else if ((!z_context)&&(r_pending)&&(s_tlb_hit)&&((!r_we)||(!ro_flag))) |
tlb_flags[s_tlb_addr][2] <= 1'b1; |
if (wr_vtable) |
tlb_flags[wr_tlb_addr] <= { i_wbs_data[3:1], 1'b0 }; |
if (wr_vtable) |
tlb_cdata[wr_tlb_addr][(LGLCTX-1):0] |
<= i_wbs_data[(LGLCTX+4-1):4]; |
tlb_cdata[wr_tlb_addr][((LGCTXT>=8)? 7:(LGCTXT-1)):0] |
<= i_wb_data[((LGCTXT>=8)? 11:(4+LGCTXT-1)):4]; |
if ((wr_ptable)&&(LGCTXT > 8)) |
tlb_cdata[wr_tlb_addr][(LGCTXT-1):8] |
<= i_wb_data[(4+LGCTXT-8-1):4]; |
setup_ack <= (i_ctrl_cyc_stb)&&(!i_reset); |
end |
|
initial tlb_accessed = 0; |
always @(posedge i_clk) |
if (i_reset) |
tlb_accessed <= 0; |
else begin |
if (wr_vtable) |
tlb_accessed[wr_tlb_addr] <= 1'b0; |
// Otherwise, keep track of the accessed bit if we |
// ever access this page |
else if ((!kernel_context)&&(pending_page_valid)) |
tlb_accessed[s_tlb_addr] <= 1'b1; |
else if ((!kernel_context)&&(this_page_valid)) |
tlb_accessed[last_tlb] <= 1'b1; |
end |
|
generate if (LGHCTX > 0) |
begin : HCTX |
always @(posedge i_clk) |
if (wr_ptable) |
tlb_cdata[wr_tlb_addr][(LGCTXT-1):LGLCTX] |
<= i_wbs_data[(LGHCTX+4-1):4]; |
end endgenerate |
|
// Writing to the control word |
initial z_context = 1'b1; |
initial r_context_word = 0; |
426,62 → 363,48
always @(posedge i_clk) |
if (wr_control) |
begin |
r_context_word <= i_wbs_data[(LGCTXT-1):0]; |
z_context <= (i_wbs_data[(LGCTXT-1):0] == {(LGCTXT){1'b0}}); |
r_context_word <= i_wb_data[(LGCTXT-1):0]; |
z_context <= (i_wb_data[(LGCTXT-1):0] == {(LGCTXT){1'b0}}); |
end |
assign kernel_context = (z_context)||(!i_gie); |
// Status words cannot be written to |
|
always @(posedge i_clk) |
if (i_reset) |
tlb_valid <= 0; |
else if (wr_ptable) |
tlb_valid[wr_tlb_addr]<=1'b1; //(i_wbs_data[(AW+1):LGPGSZB]!=0); |
|
/* v*rilator lint_off WIDTH */ |
assign w_control_data[31:28] = AW[3:0]-4'd1; |
assign w_control_data[27:24] = LGTBL[3:0]; |
assign w_control_data[23:20] = LGPGSZB[3:0]-4'd10; |
assign w_control_data[19:16] = LGCTXT[3:0]-1'b1; |
/* v*rilator lint_on WIDTH */ |
/* verilator lint_off WIDTH */ |
assign w_control_data[31:28] = AW-17; |
assign w_control_data[27:24] = LGTBL; |
assign w_control_data[23:20] = LGPGSZ-8; |
assign w_control_data[19:16] = LGCTXT-1; |
/* verilator lint_on WIDTH */ |
assign w_control_data[15: 0] = {{(16-LGCTXT){1'b0}}, r_context_word}; |
// |
always @(*) |
begin |
w_vtable_reg = 0; |
w_vtable_reg[(DW-1):LGPGSZB] = tlb_vdata[wr_tlb_addr]; |
w_vtable_reg[(LGLCTX+4-1):4] = { tlb_cdata[wr_tlb_addr][(LGLCTX-1):0] }; |
w_vtable_reg[ 3:0] = { tlb_flags[wr_tlb_addr][3:1], |
tlb_accessed[wr_tlb_addr] }; |
end |
assign w_vtable_reg[(DW-1):LGPGSZ] = tlb_vdata[wr_tlb_addr]; |
assign w_vtable_reg[(LGPGSZ-1):(LGLCTX+4-1)] = 0; |
assign w_vtable_reg[(LGLCTX+4-1):4] = { tlb_cdata[wr_tlb_addr][(LGLCTX-1):0] }; |
assign w_vtable_reg[ 3:0] = { tlb_flags[wr_tlb_addr], 1'b0 }; |
// |
assign w_ptable_reg[(DW-1):LGPGSZB] = { {(DW-PAW-LGPGSZB){1'b0}}, |
assign w_ptable_reg[(DW-1):LGPGSZ] = { {(DW-AW){1'b0}}, |
tlb_pdata[wr_tlb_addr] }; |
assign w_ptable_reg[ 3:0] = 4'h0; |
assign w_ptable_reg[LGPGSZ:(4+LGHCTX)] = 0; |
assign w_ptable_reg[ 3:0] = { tlb_flags[wr_tlb_addr], 1'b0 }; |
assign w_ctable_reg = tlb_cdata[wr_tlb_addr]; |
// |
generate |
if (4+LGHCTX-1>4) |
assign w_ptable_reg[(4+LGHCTX-1):4] = { |
tlb_cdata[wr_tlb_addr][(LGCTXT-1):LGLCTX] }; |
if (LGPGSZB > LGLCTX+4) |
assign w_vtable_reg[(LGPGSZB-1):(LGLCTX+4)] = 0; |
if (LGPGSZB > LGHCTX+4) |
assign w_ptable_reg[(LGPGSZB-1):(LGHCTX+4)] = 0; |
endgenerate |
|
// |
// Now, reading from the bus |
/* |
wire [(LGCTXT-1):0] w_ctable_reg; |
assign w_ctable_reg = tlb_cdata[wr_tlb_addr]; |
reg setup_this_page_flag; |
reg [(LGCTXT-1):0] setup_page; |
initial setup_this_page_flag = 1'b0; |
always @(posedge i_clk) |
setup_page <= w_ctable_reg; |
always @(posedge i_clk) |
setup_this_page_flag <= (!i_reset)&&(i_wbs_cyc_stb)&&(i_wbs_addr[LGTBL+1]); |
*/ |
setup_this_page_flag <= (i_ctrl_cyc_stb)&&(i_wb_addr[LGTBL+1]); |
always @(posedge i_clk) |
case({i_wb_addr[LGTBL+1],i_wb_addr[0]}) |
2'b00: setup_data <= w_control_data; |
2'b01: setup_data <= status_word; |
2'b10: setup_data <= w_vtable_reg; |
2'b11: setup_data <= w_ptable_reg; |
endcase |
|
|
|
492,6 → 415,7
// |
// |
////////////////////////////////////////// |
assign w_mmu_err_vaddr = { {(DW-AW){1'b0}}, r_mmu_err_vaddr }; |
|
// |
// |
500,280 → 424,125
// work. |
// |
// |
wire [(VAW-1):0] r_vpage; |
wire [(PAW-1):0] r_ppage; |
assign r_vpage = (r_addr[(DW-3):(DW-2-VAW)]); |
assign r_ppage = (o_addr[(AW-1):LGPGSZW]); |
|
initial s_pending = 1'b0; |
initial r_pending = 1'b0; |
initial r_valid = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
begin |
r_pending <= 1'b0; |
r_valid <= 1'b0; |
o_addr <= 0; |
r_we <= 0; |
r_exe <= 0; |
r_addr <= 0; |
r_data <= 0; |
r_sel <= 0; |
// |
s_pending <= 1'b0; |
end else |
begin |
if (!o_rtn_stall) |
begin |
r_pending <= (i_wbm_stb)&&(!kernel_context) |
&&(!this_page_valid); |
r_we <= i_wbm_we; |
r_exe <= i_wbm_exe; |
o_addr <= { { (kernel_context)? |
i_wbm_addr[(AW-1):LGPGSZW] : last_ppage }, |
i_wbm_addr[(LGPGSZW-1):0] }; |
r_addr <= i_wbm_addr; |
r_data <= i_wbm_data; |
r_sel <= i_wbm_sel; |
r_valid <= (i_wbm_stb)&&((kernel_context)||(this_page_valid)); |
r_pending <= i_wbm_stb; |
r_we <= i_wb_we; |
r_addr <= i_wb_addr; |
r_data <= i_wb_data; |
r_valid <= (i_wbm_stb)&&((z_context)||((last_page_valid) |
&&(i_wb_addr[(DW-1):LGPGSZ] == last_vpage) |
&&((!last_ro)||(!i_wb_we)))); |
s_pending <= 1'b0; |
end else if (!r_valid) begin |
r_valid <= (pending_page_valid); |
o_addr <= { ppage , r_addr[(LGPGSZW-1):0] }; |
r_pending<= (r_pending)&&(!pending_page_valid); |
s_pending <=(r_pending)&&(!pending_page_valid); |
end else begin |
r_pending <= 1'b0; |
s_pending <= 1'b0; |
r_valid <= (r_valid)||((last_page_valid) |
&&(r_addr[(DW-1):LGPGSZ] == last_vpage) |
&&((!last_ro)||(!r_we))); |
r_pending<= (r_pending)&&(i_wbm_cyc); |
s_pending <= r_pending; |
end |
|
if ((!i_wbm_cyc)||(o_rtn_err)||((o_cyc)&&(i_err))) |
begin |
s_pending <= 1'b0; |
if (i_reset) |
r_pending <= 1'b0; |
r_valid <= 1'b0; |
end |
end |
|
`ifdef FORMAL |
reg f_past_valid; |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(r_pending))&&(r_pending)&&($past(o_rtn_stall))&&(i_wbm_cyc)&&(!o_stb)) |
assert(s_pending); |
`endif |
|
// Second clock: know which buffer entry this belong in. |
// If we don't already know, then the pipeline must be stalled for a |
// while ... |
genvar k, s; |
generate |
for(k=0; k<TBL_SIZE; k = k + 1) |
for(k=0; k<TBL_BITS; k = k + 1) |
assign r_tlb_match[k] = |
// The page must be valid |
(tlb_valid[k]) |
// Virtual address must match |
&&(tlb_vdata[k] == r_vpage) |
((tlb_vdata[k] == r_addr[(DW-1):LGPGSZ]) |
// Context must match as well |
&&(tlb_cdata[k][LGCTXT-1:1] == r_context_word[LGCTXT-1:1]) |
&&((!tlb_cdata[k][0])||(r_context_word[0])); |
&&(tlb_cdata[k] == r_context_word)); |
endgenerate |
|
initial s_tlb_miss = 1'b0; |
initial s_tlb_hit = 1'b0; |
generate |
integer i; |
always @(posedge i_clk) |
begin // valid when s_ becomes valid |
s_tlb_addr <= {(LGTBL){1'b0}}; |
for(i=0; i<TBL_SIZE; i=i+1) |
if (r_tlb_match[i]) |
s_tlb_addr <= i[(LGTBL-1):0]; |
s_tlb_miss <= (r_pending)&&(r_tlb_match == 0); |
for(k=0; k<TBL_SIZE; k=k+1) |
for(s=0; s<LGTBL; s=s+1) |
if (((k&(1<<s))!=0)&&(r_tlb_match[k])) |
s_tlb_addr[s] <= 1'b1; |
s_tlb_miss <= (r_pending)&&(r_tlb_match[(TBL_BITS-1):0] == 0); |
s_tlb_hit <= 1'b0; |
for(i=0; i<TBL_SIZE; i=i+1) |
if (r_tlb_match == (1<<i)) |
s_tlb_hit <= (r_pending)&&(!r_valid)&&(i_wbm_cyc); |
for(k=0; k<TBL_SIZE; k=k+1) |
if (r_tlb_match == (1<<k)) |
s_tlb_hit <= (r_pending); |
end endgenerate |
|
|
// Third clock: Read from the address the virtual table offset, |
// whether read-only, etc. |
assign s_tlb_flags = tlb_flags[s_tlb_addr]; |
assign ro_flag = s_tlb_flags[`ROFLAG]; |
assign exe_flag = s_tlb_flags[`EXEFLG]; |
assign cachable = s_tlb_flags[`CHFLAG]; |
assign ro_flag = tlb_flags[s_tlb_addr][3]; |
assign simple_miss = (s_pending)&&(s_tlb_miss); |
assign ro_miss = (s_pending)&&(s_tlb_hit)&&(r_we)&&(ro_flag); |
assign exe_miss = (s_pending)&&(s_tlb_hit)&&(r_exe)&&(!exe_flag); |
assign table_err = (s_pending)&&(!s_tlb_miss)&&(!s_tlb_hit); |
assign vpage = tlb_vdata[s_tlb_addr]; |
assign ppage = tlb_pdata[s_tlb_addr]; |
|
initial pf_cachable = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
pf_cachable <= 1'b0; |
else |
pf_cachable <= cachable; |
|
assign cachable = tlb_flags[s_tlb_addr][1]; |
// assign tlb_access_flag = tlb_flags[s_tlb_addr][2]; |
initial pf_stb = 1'b0; |
initial last_ppage = 0; |
initial last_vpage = 0; |
initial p_tlb_err = 1'b0; |
initial p_tlb_miss = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
begin |
pf_stb <= 1'b0; |
last_ppage <= 0; |
last_vpage <= 0; |
last_tlb <= 0; |
end else if ((!kernel_context)&&(r_pending)&&(!last_page_valid)) |
begin |
last_tlb <= s_tlb_addr; |
last_ppage <= ppage; |
last_vpage <= vpage; |
last_exe <= exe_flag; |
last_ro <= ro_flag; |
pf_stb <= 1'b1; |
end else |
pf_stb <= 1'b0; |
p_tlb_miss <= (simple_miss)||(ro_miss); |
p_tlb_err <= (s_pending)&&((!s_tlb_miss)&&(!s_tlb_hit)); |
|
initial status_word = 0; |
always @(posedge i_clk) |
if (i_reset) |
status_word <= 0; |
else if (wr_control) |
status_word <= 0; |
else if ((table_err)||(ro_miss)||(simple_miss)||(exe_miss)) |
status_word <= { r_vpage, |
{(LGPGSZB-4){1'b0}}, |
(table_err), (exe_miss), |
(ro_miss), (simple_miss) }; |
pf_cachable <= cachable; |
if ((!z_context)&&(r_pending)) |
begin |
last_ppage <= tlb_pdata[s_tlb_addr]; |
last_vpage <= tlb_vdata[s_tlb_addr]; |
last_ro <= ro_flag; |
pf_stb <= 1'b1; |
end else |
pf_stb <= 1'b0; |
if ((table_err)||(ro_miss)||(simple_miss)) |
status_word <= { r_addr[(DW-1):LGPGSZ], |
{(LGPGSZ-3){1'b0}}, |
(table_err), (ro_miss), (simple_miss) }; |
|
if (wr_control) |
last_page_valid <= (last_page_valid) |
&&(r_context_word == i_wb_data[(LGCTXT-1):0]); |
else if ((r_pending)&&(!z_context)) |
last_page_valid <= (s_tlb_hit)&&(!ro_miss); |
|
initial last_page_valid = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
last_page_valid <= 1'b0; |
else if ((i_wbs_cyc_stb)&&(i_wbs_we)) |
last_page_valid <= 1'b0; |
else if (!kernel_context) |
begin |
if (!o_rtn_stall) |
// A new bus request |
last_page_valid <= (last_page_valid) |
&&(i_wbm_addr[(DW-3):(DW-2-VAW)] == last_vpage); |
else if ((r_pending)&&(!last_page_valid)) |
last_page_valid <= (s_pending)&&(s_tlb_hit); |
if (i_reset) |
last_page_valid <= 1'b0; |
end |
|
parameter LGFIFO = 6; |
reg [LGFIFO-1:0] bus_outstanding; |
initial bus_outstanding = 0; |
always @(posedge i_clk) |
if (i_reset) |
bus_outstanding <= 0; |
else if (!o_cyc) |
bus_outstanding <= 0; |
else case({ (o_stb)&&(!i_stall), (i_ack)||(i_err) } ) |
2'b01: bus_outstanding <= bus_outstanding - 1'b1; |
2'b10: bus_outstanding <= bus_outstanding + 1'b1; |
default: begin end |
endcase |
|
reg bus_pending; |
initial bus_pending = 0; |
always @(posedge i_clk) |
if (i_reset) |
bus_pending <= 0; |
else if (!o_cyc) |
bus_pending <= 1'b0; |
else case({ (o_stb)&&(!i_stall), ((i_ack)||(i_err)) }) |
2'b01: bus_pending <= (bus_outstanding > 1); |
2'b10: bus_pending <= 1'b1; |
default: begin end |
endcase |
|
initial rtn_err = 1'b0; |
initial o_cyc = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
begin |
o_cyc <= 1'b0; |
rtn_err <= 1'b0; |
end else begin |
o_cyc <= (i_wbm_cyc)&&(!o_rtn_err)&&((!i_err)||(!o_cyc)); /// &&((o_cyc)||(r_valid)); |
o_cyc <= (!i_reset)&&(i_wbm_cyc); |
|
rtn_err <= (i_wbm_cyc)&&(i_err)&&(o_cyc); |
o_rtn_ack <= (!i_reset)&&((setup_ack)||(i_wbm_cyc)&&(i_ack)); |
o_rtn_data <= (setup_ack) ? setup_data : i_data; |
if (setup_this_page_flag) |
o_rtn_data[0] <= ((setup_page == r_context_word)? 1'b1:1'b0); |
rtn_err <= (!i_reset)&&(i_wbm_cyc)&&(i_err); |
end |
|
generate if (OPT_DELAY_RETURN) |
begin |
reg r_rtn_ack; |
reg [31:0] r_rtn_data; |
|
initial r_rtn_data = 0; |
initial r_rtn_ack = 0; |
always @(posedge i_clk) |
if (i_reset) |
begin |
r_rtn_ack <= 0; |
r_rtn_data <= 0; |
end else begin |
r_rtn_ack <= (i_wbm_cyc)&&(i_ack)&&(o_cyc); |
r_rtn_data <= i_data; |
end |
|
assign o_rtn_ack = r_rtn_ack; |
assign o_rtn_data = r_rtn_data; |
end else begin |
|
assign o_rtn_ack = (i_ack)&&(o_cyc); |
assign o_rtn_data = i_data; |
end endgenerate |
|
assign o_stb = (r_valid); |
assign o_we = (r_we); |
assign o_rtn_stall = (i_wbm_cyc)&&( |
(o_rtn_err) |
||((r_pending)&&(!r_valid)) |
||((o_stb)&&(i_stall)) |
||(miss_pending)); |
assign o_rtn_stall = (i_wbm_cyc)&&(((r_pending)&&(!r_valid))||(i_stall)); |
assign o_rtn_miss = p_tlb_miss; |
assign o_rtn_err = (rtn_err)||(p_tlb_err); |
|
initial miss_pending = 0; |
always @(posedge i_clk) |
if (i_reset) |
miss_pending <= 0; |
else if (!i_wbm_cyc) |
miss_pending <= 0; |
else |
miss_pending <= (i_wbm_cyc)&&( |
(simple_miss)||(ro_miss)||(exe_miss) |
||((s_pending)&&(!s_tlb_miss)&&(!s_tlb_hit))); |
|
assign o_rtn_miss = (miss_pending)&&(!bus_pending); |
assign o_rtn_err = (rtn_err); |
|
assign o_sel = r_sel; |
assign o_addr[(AW-1):0] = {(z_context)? |
r_addr[(AW-1):LGPGSZ] : last_ppage, |
r_addr[(LGPGSZ-1):0]}; |
assign o_data = r_data; |
|
// |
assign o_wbs_stall = 1'b0; |
initial o_wbs_ack = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
o_wbs_ack <= 1'b0; |
else |
o_wbs_ack <= (i_wbs_cyc_stb); |
always @(posedge i_clk) |
if (i_reset) |
o_wbs_data <= 0; |
else case({i_wbs_addr[LGTBL+1],i_wbs_addr[0]}) |
2'b00: o_wbs_data <= w_control_data; |
2'b01: o_wbs_data <= status_word; |
2'b10: o_wbs_data <= w_vtable_reg; |
2'b11: o_wbs_data <= w_ptable_reg; |
endcase |
|
// |
// Bus snooping returns ... |
// |
assign pf_return_stb = pf_stb; |
782,378 → 551,4
assign pf_return_v = last_vpage; |
assign pf_return_cachable = pf_cachable; |
|
// Also requires being told when/if the page changed |
// So, on a page change, |
// pf_return_we = 1 |
// pf_stb = 1 |
// and pf_return_p has the physical address |
|
// Make verilator happy |
// verilator lint_off UNUSED |
wire [(PAW-1):0] unused; |
assign unused = r_ppage; |
generate if (4+LGCTXT < LGPGSZB) |
begin |
wire [LGPGSZB-(4+LGCTXT)-1:0] unused_data; |
assign unused_data = i_wbs_data[LGPGSZB-1:4+LGCTXT]; |
end endgenerate |
|
wire unused_always; |
assign unused_always = s_tlb_flags[0]; |
// verilator lint_on UNUSED |
|
`ifdef FORMAL |
initial f_past_valid = 0; |
always @(posedge i_clk) |
f_past_valid <= 1'b1; |
|
initial assume(i_reset); |
always @(*) |
if (!f_past_valid) |
assume(i_reset); |
|
always @(*) |
if (i_reset) |
assume(!i_wbs_cyc_stb); |
always @(posedge i_clk) |
if (f_past_valid) |
assert(o_wbs_ack == $past(i_wbs_cyc_stb)); |
always @(*) |
assert(o_wbs_stall == 1'b0); |
|
always @(*) |
assume((!i_wbm_cyc)||(!i_wbs_cyc_stb)); |
|
localparam F_LGDEPTH = 6; |
reg [F_LGDEPTH-1:0] fv_nreqs, fv_nacks, fv_outstanding, |
fp_nreqs, fp_nacks, fp_outstanding; |
|
localparam F_MAX_STALL = 3, |
F_MAX_WAIT = 2, |
F_MAX_REQ = 9; |
|
// |
// The stall period needs to be long enough to allow all in-progress |
// transactions to complete, as in the case of a page miss. Hence, |
// the max stall amount depends upon the max wait time for the |
// physical half of the interaction. It is artificially limited here |
// in order to limit the amount of proof time required. |
// |
fwb_slave #(.F_MAX_STALL(F_MAX_STALL+(F_MAX_WAIT*F_MAX_REQ)+2), |
.AW(DW-2), |
.F_MAX_ACK_DELAY(F_MAX_STALL+F_MAX_WAIT+5), |
.F_MAX_REQUESTS(F_MAX_REQ), |
.F_LGDEPTH(F_LGDEPTH), |
.F_OPT_MINCLOCK_DELAY(0)) |
busslave(i_clk, i_reset, |
i_wbm_cyc, i_wbm_stb, i_wbm_we, i_wbm_addr, |
i_wbm_data, i_wbm_sel, |
o_rtn_ack, o_rtn_stall, o_rtn_data, |
o_rtn_err|o_rtn_miss, |
fv_nreqs, fv_nacks, fv_outstanding); |
|
fwb_master #(.F_MAX_STALL(F_MAX_STALL), |
.AW(ADDRESS_WIDTH), |
.F_MAX_ACK_DELAY(F_MAX_WAIT), |
.F_MAX_REQUESTS(F_MAX_REQ), |
.F_LGDEPTH(F_LGDEPTH), |
.F_OPT_MINCLOCK_DELAY(0)) |
busmaster(i_clk, i_reset, |
o_cyc, o_stb, o_we, o_addr, |
o_data, o_sel, |
i_ack, i_stall, i_data, i_err, |
fp_nreqs, fp_nacks, fp_outstanding); |
|
always @(*) |
assert((!o_cyc)||(fp_outstanding == bus_outstanding)); |
|
always @(*) |
assume(fv_nreqs < F_MAX_REQ); |
always @(*) |
if ((i_wbm_cyc)&&(o_cyc)&&(fv_outstanding == fp_outstanding)) |
assert(fv_nreqs == fp_nreqs); |
always @(*) |
if ((i_wbm_cyc)&&(o_cyc)) |
begin |
assert(fp_nreqs <= fv_nreqs); |
assert(fp_nacks >= fv_nacks); |
end |
|
reg [F_LGDEPTH-1:0] f_expected, f_ex_nreqs, f_ex_nacks; |
always @(*) |
if (!i_wbm_cyc) |
begin |
f_ex_nreqs <= 0; |
f_ex_nacks <= 0; |
f_expected <= 0; |
end else if (OPT_DELAY_RETURN) |
begin |
if (r_pending) |
begin |
f_ex_nreqs <= fp_nreqs + 1'b1; |
f_ex_nacks <= fp_nacks + o_rtn_ack; |
f_expected <= fp_outstanding + 1'b1 |
+ o_rtn_ack; |
end else begin |
f_expected <= fp_outstanding + (o_stb) |
+ (o_rtn_ack); |
f_ex_nreqs <= fp_nreqs + o_stb; |
f_ex_nacks <= fp_nacks + o_rtn_ack; |
end |
end else begin |
if (r_pending) |
begin |
f_ex_nreqs <= fp_nreqs + 1'b1; |
f_ex_nacks <= fp_nacks; |
f_expected <= fp_outstanding + 1'b1; |
end else begin |
f_ex_nreqs <= fp_nreqs + o_stb; |
f_ex_nacks <= fp_nacks; |
f_expected <= fp_outstanding + (o_stb); |
end |
end |
|
reg f_kill_input; |
initial f_kill_input = 1'b0; |
always @(posedge i_clk) |
f_kill_input <= (i_wbm_cyc)&&( |
(i_reset) |
||(o_rtn_miss) |
||(o_rtn_err)); |
always @(*) |
if (f_kill_input) |
assume(!i_wbm_cyc); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_rtn_miss))&&($past(i_wbm_cyc))) |
begin |
assume(!o_cyc); |
assume(!i_wbm_cyc); |
end |
|
wire fv_is_one, fp_is_zero; |
assign fv_is_one = (fv_outstanding == 1); |
assign fp_is_zero = (fp_outstanding == 0); |
always @(*) |
if ((i_wbm_cyc)&&(o_cyc)) |
begin |
if (o_rtn_miss) |
begin |
assert(fp_outstanding == 0); |
assert(fv_outstanding == 1); |
assert(fv_is_one); |
assert(fp_is_zero); |
end else begin |
assert(fv_nreqs == f_ex_nreqs); |
assert(fv_nacks == f_ex_nacks); |
assert(fv_outstanding >= fp_outstanding); |
assert(fv_outstanding == f_expected); |
end |
end |
|
always @(*) |
assert(z_context == (r_context_word == 0)); |
always @(*) |
assert(kernel_context == ( ((r_context_word == 0)||(!i_gie)) ? 1'b1 : 1'b0)); |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_wbs_cyc_stb))) |
assume(!i_wbm_cyc); |
always @(*) |
if (o_wbs_ack) |
assume(!i_wbm_cyc); |
|
always @(*) |
assert((!i_wbm_cyc)||(!o_wbs_ack)); |
always @(posedge i_clk) |
if ((f_past_valid)&&(r_pending)&&($past(kernel_context)) |
&&($past(i_wbm_stb))&&(!$past(i_stall))&&(i_wbm_cyc) |
&&(!o_rtn_stall)) |
assert(o_addr[(AW-1):0] == $past(i_wbm_addr[(AW-1):0])); |
always @(*) |
assert(bus_pending == (bus_outstanding > 0)); |
|
always @(*) |
if ((s_pending)&&(!s_tlb_miss)) |
assert(r_tlb_match[s_tlb_addr]); |
|
// Check out all of the criteria which should clear these flags |
always @(posedge i_clk) |
if ((f_past_valid)&&(($past(i_reset)) |
||(!$past(i_wbm_cyc)) |
||(!$past(o_rtn_stall)))) |
begin |
assert(!simple_miss); |
assert(!ro_miss); |
assert(!exe_miss); |
assert(!table_err); |
if (!$past(i_wbm_we)) |
assert(!ro_miss); |
|
if (!kernel_context) |
begin |
assert((!o_stb)||(!(simple_miss|ro_miss|table_err))); |
// This doesn't belong on the clear list, but on the |
// should be set list |
// assert((!o_stb)||(!s_tlb_hit)); |
end |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wbm_cyc)) |
&&(!$past(o_rtn_stall))) |
begin |
if ((!$past(kernel_context))&&(o_stb)) |
assert((last_page_valid)||(s_tlb_hit)); |
end |
|
reg [(LGTBL-1):0] f_last_page; |
always @(posedge i_clk) |
if ((f_past_valid)&&(!kernel_context)&&(r_pending)&&(!last_page_valid)) |
f_last_page <= s_tlb_addr; |
|
wire [3:0] tlb_flag_last_page; |
assign tlb_flag_last_page = tlb_flags[f_last_page]; |
always @(*) |
if (last_page_valid) |
begin |
assert(tlb_valid[f_last_page]); |
assert(last_tlb == f_last_page); |
assert(last_ppage == tlb_pdata[f_last_page]); |
assert(last_vpage == tlb_vdata[f_last_page]); |
assert(last_ro == tlb_flag_last_page[`ROFLAG]); |
assert(last_exe == tlb_flag_last_page[`EXEFLG]); |
assert(r_context_word[LGCTXT-1:1] == tlb_cdata[f_last_page][LGCTXT-1:1]); |
if (!r_context_word[0]) |
assert(!tlb_cdata[f_last_page][0]); |
assert((!r_context_word[0])||(r_context_word[0])); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset)) |
&&($past(last_page_valid))&&(!$past(kernel_context)) |
&&($past(o_stb))&&($past(i_wbm_cyc))) |
assert(tlb_accessed[$past(last_tlb)]); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset)) |
&&($past(pending_page_valid))&&(!$past(kernel_context)) |
&&($past(o_stb))&&($past(i_wbm_cyc))) |
assert(tlb_accessed[$past(s_tlb_addr)]); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(kernel_context))&&(o_stb)) |
begin |
assert(last_page_valid); |
assert(r_ppage == last_ppage); |
assert((!last_ro)||(!o_we)); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(o_stb))&&(o_stb)&&(i_wbm_cyc)) |
assert((last_page_valid)||(kernel_context)); |
|
always @(*) |
assert((!s_tlb_hit)||(!s_tlb_miss)); |
// always @(*) |
// if ((fp_outstanding > 0)&&(o_cyc)&&(!o_stb)&&(!r_pending)&&(!kernel_context)) |
// assert(last_page_valid); |
// always @(*) assume(kernel_context); |
always @(*) |
assume((!i_wbs_cyc_stb)||(!i_gie)); |
|
reg f_past_gie, f_past_wbm_cyc; |
|
initial f_past_gie = 1'b0; |
always @(posedge i_clk) |
f_past_gie <= i_gie; |
|
initial f_past_wbm_cyc = 1'b0; |
always @(posedge i_clk) |
f_past_wbm_cyc <= i_wbm_cyc; |
always @(*) |
if ((f_past_valid)&&(bus_pending)) |
assume(i_gie == f_past_gie); |
always @(*) |
if ((f_past_wbm_cyc)&&(i_wbm_cyc)) |
assume(i_gie == f_past_gie); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(i_wbm_cyc)&&($past(i_wbm_cyc))) |
assume(i_gie == $past(i_gie)); |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_reset))) |
assume(!i_gie); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wbm_cyc)) |
&&($past(!kernel_context)) |
&&($past(r_pending)) |
&&(!$past(last_page_valid))) |
begin |
if (($past(s_tlb_hit)) |
&&(!$past(ro_miss)) |
&&(!$past(exe_miss))) |
begin |
assert(last_vpage == $past(r_vpage)); |
assert(last_page_valid); |
assert(!miss_pending); |
assert(tlb_accessed[s_tlb_addr]); |
end else if (($past(s_tlb_hit))&&($past(ro_miss))) |
begin |
assert(miss_pending); |
assert(last_page_valid); |
assert(status_word[3:0] == 4'h2); |
end else if (($past(s_tlb_hit))&&($past(exe_miss))) |
begin |
assert(miss_pending); |
assert(last_page_valid); |
assert(status_word[3:0] == 4'h4); |
end else if (($past(s_tlb_hit))&&($past(simple_miss))) |
begin |
assert(miss_pending); |
assert(last_page_valid); |
assert(status_word[3:0] == 4'h1); |
end else if (!$past(s_tlb_hit)) |
begin |
assert(!last_page_valid); |
end |
end |
|
always @(*) |
assert((!ro_miss)||(!exe_miss)||(!simple_miss)||(!table_err)); |
|
reg [4:0] f_tlb_pipe; |
|
initial f_tlb_pipe = 5'h0; |
always @(posedge i_clk) |
if (i_reset) |
f_tlb_pipe <= 5'h0; |
else if ((!r_pending)||(o_stb)) |
f_tlb_pipe <= 5'h0; |
else if ((r_pending)&&(!r_valid)&&(!miss_pending)) |
f_tlb_pipe <= { f_tlb_pipe[3:0], 1'b1 }; |
|
always @(*) |
assert(f_tlb_pipe != 5'h1f); |
|
always @(*) // WE or EXE, never both |
assume((!i_wbm_stb)||(!i_wbm_we)||(!i_wbm_exe)); |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_wbm_stb))&&($past(o_rtn_stall))) |
assume(i_wbm_exe == $past(i_wbm_exe)); |
|
always @(*) |
assert((!r_pending)||(!o_stb)); |
always @(*) |
assert((!s_pending)||(!o_stb)); |
always @(*) |
assert((!s_pending)||(r_pending)); |
always @(posedge i_clk) |
if ((f_past_valid)&&($past(i_wbm_cyc))) |
assume(!i_wbs_cyc_stb); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(|status_word[3:0])&&(!$past(i_wbm_cyc))) |
assume(!i_gie); |
`endif |
endmodule |
/peripherals/ziptimer.v
45,7 → 45,7
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015,2017-2019, Gisselquist Technology, LLC |
// Copyright (C) 2015,2017, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
69,17 → 69,15
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
// |
module ziptimer(i_clk, i_reset, i_ce, |
module ziptimer(i_clk, i_rst, i_ce, |
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_data, |
o_wb_ack, o_wb_stall, o_wb_data, |
o_int); |
parameter BW = 32, VW = (BW-1), RELOADABLE=1; |
input wire i_clk, i_reset, i_ce; |
input i_clk, i_rst, i_ce; |
// Wishbone inputs |
input wire i_wb_cyc, i_wb_stb, i_wb_we; |
input wire [(BW-1):0] i_wb_data; |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [(BW-1):0] i_wb_data; |
// Wishbone outputs |
output reg o_wb_ack; |
output wire o_wb_stall; |
90,34 → 88,31
reg r_running; |
|
wire wb_write; |
assign wb_write = ((i_wb_stb)&&(i_wb_we)); |
assign wb_write = ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_we)); |
|
wire auto_reload; |
wire [(VW-1):0] interval_count; |
wire auto_reload; |
wire [(VW-1):0] reload_value; |
|
initial r_running = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
if (i_rst) |
r_running <= 1'b0; |
else if (wb_write) |
r_running <= (|i_wb_data[(VW-1):0]); |
else if ((r_zero)&&(!auto_reload)) |
else if ((o_int)&&(~auto_reload)) |
r_running <= 1'b0; |
|
generate |
if (RELOADABLE != 0) |
begin |
reg r_auto_reload; |
reg [(VW-1):0] r_interval_count; |
reg r_auto_reload; |
reg [(VW-1):0] r_reload_value; |
|
initial r_auto_reload = 1'b0; |
|
always @(posedge i_clk) |
if (i_reset) |
r_auto_reload <= 1'b0; |
else if (wb_write) |
r_auto_reload <= (i_wb_data[(BW-1)]) |
&&(|i_wb_data[(VW-1):0]); |
if (wb_write) |
r_auto_reload <= (i_wb_data[(BW-1)]); |
|
assign auto_reload = r_auto_reload; |
|
124,12 → 119,12
// If setting auto-reload mode, and the value to other |
// than zero, set the auto-reload value |
always @(posedge i_clk) |
if (wb_write) |
r_interval_count <= i_wb_data[(VW-1):0]; |
assign interval_count = r_interval_count; |
if ((wb_write)&&(i_wb_data[(BW-1)])&&(|i_wb_data[(VW-1):0])) |
r_reload_value <= i_wb_data[(VW-1):0]; |
assign reload_value = r_reload_value; |
end else begin |
assign auto_reload = 1'b0; |
assign interval_count = 0; |
assign reload_value = 0; |
end endgenerate |
|
|
136,44 → 131,27
reg [(VW-1):0] r_value; |
initial r_value = 0; |
always @(posedge i_clk) |
if (i_reset) |
r_value <= 0; |
else if (wb_write) |
if (wb_write) |
r_value <= i_wb_data[(VW-1):0]; |
else if ((i_ce)&&(r_running)) |
begin |
if (!r_zero) |
r_value <= r_value - 1'b1; |
else if (auto_reload) |
r_value <= interval_count; |
end |
else if ((r_running)&&(i_ce)&&(~o_int)) |
r_value <= r_value + {(VW){1'b1}}; // r_value - 1; |
else if ((r_running)&&(auto_reload)&&(o_int)) |
r_value <= reload_value; |
|
reg r_zero = 1'b1; |
always @(posedge i_clk) |
if (i_reset) |
r_zero <= 1'b1; |
else if (wb_write) |
r_zero <= (i_wb_data[(VW-1):0] == 0); |
else if ((r_running)&&(i_ce)) |
begin |
if (r_value == { {(VW-1){1'b0}}, 1'b1 }) |
r_zero <= 1'b1; |
else if ((r_zero)&&(auto_reload)) |
r_zero <= 1'b0; |
end |
|
// Set the interrupt on our last tick, as we transition from one to |
// zero. |
initial o_int = 1'b0; |
always @(posedge i_clk) |
if ((i_reset)||(wb_write)||(!i_ce)) |
if (i_rst) |
o_int <= 1'b0; |
else // if (i_ce) |
o_int <= (r_value == { {(VW-1){1'b0}}, 1'b1 }); |
else if (i_ce) |
o_int <= (r_running)&&(r_value == { {(VW-1){1'b0}}, 1'b1 }); |
else |
o_int <= 1'b0; |
|
initial o_wb_ack = 1'b0; |
always @(posedge i_clk) |
o_wb_ack <= (!i_reset)&&(i_wb_stb); |
o_wb_ack <= (i_wb_cyc)&&(i_wb_stb); |
assign o_wb_stall = 1'b0; |
|
generate |
183,103 → 161,4
assign o_wb_data = { auto_reload, r_value }; |
endgenerate |
|
// Make verilator happy |
// verilator lint_off UNUSED |
wire [32:0] unused; |
assign unused = { i_wb_cyc, i_wb_data }; |
// verilator lint_on UNUSED |
|
`ifdef FORMAL |
reg f_past_valid; |
initial f_past_valid = 1'b0; |
always @(posedge i_clk) |
f_past_valid <= 1'b1; |
initial assume(i_reset); |
always @(*) |
if (!f_past_valid) |
assume(i_reset); |
|
always @(posedge i_clk) |
if ((!f_past_valid)||($past(i_reset))) |
begin |
assert(r_value == 0); |
assert(r_running == 0); |
assert(auto_reload == 0); |
assert(r_zero == 1'b1); |
end |
|
|
always @(*) |
assert(r_zero == (r_value == 0)); |
|
always @(*) |
if (r_value != 0) |
assert(r_running); |
|
always @(*) |
if (auto_reload) |
assert(r_running); |
|
always @(*) |
if (!RELOADABLE) |
assert(auto_reload == 0); |
|
always @(*) |
if (auto_reload) |
assert(interval_count != 0); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&($past(r_value)==0) |
&&(!$past(wb_write))&&(!$past(auto_reload))) |
assert(r_value == 0); |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&(!$past(wb_write)) |
&&($past(r_value)==0)&&($past(auto_reload))) |
begin |
if ($past(i_ce)) |
assert(r_value == interval_count); |
else |
assert(r_value == $past(r_value)); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset)) |
&&(!$past(wb_write))&&($past(r_value)!=0)) |
begin |
if ($past(i_ce)) |
assert(r_value == $past(r_value)-1'b1); |
else |
assert(r_value == $past(r_value)); |
end |
|
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(wb_write))) |
assert(r_value == $past(i_wb_data[(VW-1):0])); |
always @(posedge i_clk) |
if ((f_past_valid)&&(!$past(i_reset))&&($past(wb_write)) |
&&(RELOADABLE)&&(|$past(i_wb_data[(VW-1):0]))) |
assert(auto_reload == $past(i_wb_data[(BW-1)])); |
|
always @(posedge i_clk) |
if (!(f_past_valid)||($past(i_reset))) |
assert(!o_int); |
else if (($past(wb_write))||(!$past(i_ce))) |
assert(!o_int); |
else |
assert(o_int == ((r_running)&&(r_value == 0))); |
|
always @(posedge i_clk) |
if ((!f_past_valid)||($past(i_reset))) |
assert(!o_wb_ack); |
else if ($past(i_wb_stb)) |
assert(o_wb_ack); |
|
always @(*) |
assert(!o_wb_stall); |
always @(*) |
assert(o_wb_data[BW-1] == auto_reload); |
always @(*) |
assert(o_wb_data[VW-1:0] == r_value); |
`endif |
endmodule |
/zipbones.v
13,7 → 13,7
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2019, Gisselquist Technology, LLC |
// Copyright (C) 2015, 2017, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
26,7 → 26,7
// for more details. |
// |
// You should have received a copy of the GNU General Public License along |
// with this program. (It's in the $(ROOT)/doc directory. Run make with no |
// with this program. (It's in the $(ROOT)/doc directory, run make with no |
// target there if the PDF file isn't present.) If not, see |
// <http://www.gnu.org/licenses/> for a copy. |
// |
37,16 → 37,9
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
// |
`include "cpudefs.v" |
// |
`define RESET_BIT 6 |
`define STEP_BIT 8 |
`define HALT_BIT 10 |
`define CLEAR_CACHE_BIT 11 |
// |
module zipbones(i_clk, i_reset, |
module zipbones(i_clk, i_rst, |
// Wishbone master interface from the CPU |
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, o_wb_sel, |
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err, |
58,78 → 51,46
i_dbg_cyc, i_dbg_stb, i_dbg_we, i_dbg_addr, i_dbg_data, |
o_dbg_ack, o_dbg_stall, o_dbg_data |
`ifdef DEBUG_SCOPE |
, o_cpu_debug |
, o_zip_debug |
`endif |
); |
parameter RESET_ADDRESS=32'h0100000, ADDRESS_WIDTH=30, |
LGICACHE=8; |
parameter [0:0] START_HALTED=0; |
parameter EXTERNAL_INTERRUPTS=1, |
`ifdef OPT_MULTIPLY |
IMPLEMENT_MPY = `OPT_MULTIPLY; |
`else |
IMPLEMENT_MPY = 0; |
`endif |
parameter [0:0] |
`ifdef OPT_DIVIDE |
IMPLEMENT_DIVIDE=1, |
`else |
IMPLEMENT_DIVIDE=0, |
`endif |
`ifdef OPT_IMPLEMENT_FPU |
IMPLEMENT_FPU=1, |
`else |
IMPLEMENT_FPU=0, |
`endif |
IMPLEMENT_LOCK=1; |
localparam // Derived parameters |
PHYSICAL_ADDRESS_WIDTH=ADDRESS_WIDTH, |
PAW=ADDRESS_WIDTH, |
`ifdef OPT_MMU |
VIRTUAL_ADDRESS_WIDTH=30, |
`else |
VIRTUAL_ADDRESS_WIDTH=PAW, |
`endif |
LGTLBSZ = 6, |
VAW=VIRTUAL_ADDRESS_WIDTH; |
|
LGICACHE=8, START_HALTED=0; |
localparam AW=ADDRESS_WIDTH; |
input wire i_clk, i_reset; |
input i_clk, i_rst; |
// Wishbone master |
output wire o_wb_cyc, o_wb_stb, o_wb_we; |
output wire [(PAW-1):0] o_wb_addr; |
output wire [(AW-1):0] o_wb_addr; |
output wire [31:0] o_wb_data; |
output wire [3:0] o_wb_sel; |
input wire i_wb_ack, i_wb_stall; |
input wire [31:0] i_wb_data; |
input wire i_wb_err; |
input i_wb_ack, i_wb_stall; |
input [31:0] i_wb_data; |
input i_wb_err; |
// Incoming interrupts |
input wire i_ext_int; |
input i_ext_int; |
// Outgoing interrupt |
output wire o_ext_int; |
// Wishbone slave |
input wire i_dbg_cyc, i_dbg_stb, i_dbg_we, i_dbg_addr; |
input wire [31:0] i_dbg_data; |
output wire o_dbg_ack; |
input i_dbg_cyc, i_dbg_stb, i_dbg_we, i_dbg_addr; |
input [31:0] i_dbg_data; |
output reg o_dbg_ack; |
output wire o_dbg_stall; |
output wire [31:0] o_dbg_data; |
// |
`ifdef DEBUG_SCOPE |
output wire [31:0] o_cpu_debug; |
output wire [31:0] o_zip_debug; |
`endif |
wire dbg_cyc, dbg_stb, dbg_we, dbg_addr, dbg_stall; |
wire [31:0] dbg_idata, dbg_odata; |
reg dbg_ack; |
|
assign dbg_cyc = i_dbg_cyc; |
assign dbg_stb = i_dbg_stb; |
assign dbg_we = i_dbg_we; |
assign dbg_addr = i_dbg_addr; |
assign dbg_idata = i_dbg_data; |
assign o_dbg_ack = dbg_ack; |
assign o_dbg_stall = dbg_stall; |
assign o_dbg_data = dbg_odata; |
// |
// |
// |
wire sys_cyc, sys_stb, sys_we; |
wire [4:0] sys_addr; |
wire [(AW-1):0] cpu_addr; |
wire [31:0] sys_data; |
wire sys_ack, sys_stall; |
|
// |
// The external debug interface |
// |
// We offer only a limited interface here, requiring a pre-register |
146,48 → 107,51
reg cmd_reset, cmd_halt, cmd_step, cmd_clear_pf_cache; |
reg [4:0] cmd_addr; |
wire [3:0] cpu_dbg_cc; |
assign dbg_cmd_write = (dbg_stb)&&(dbg_we)&&(!dbg_addr); |
assign dbg_cmd_write = (i_dbg_cyc)&&(i_dbg_stb)&&(i_dbg_we)&&(~i_dbg_addr); |
// |
// Always start us off with an initial reset |
// |
initial cmd_reset = 1'b1; |
always @(posedge i_clk) |
cmd_reset <= ((dbg_cmd_write)&&(dbg_idata[`RESET_BIT])); |
cmd_reset <= ((dbg_cmd_write)&&(i_dbg_data[6])); |
// |
initial cmd_halt = START_HALTED; |
always @(posedge i_clk) |
if (i_reset) |
cmd_halt <= START_HALTED; |
else if (cmd_reset) |
cmd_halt <= START_HALTED; |
else if (dbg_cmd_write) |
cmd_halt <= ((dbg_idata[`HALT_BIT])&&(!dbg_idata[`STEP_BIT])); |
else if ((cmd_step)||(cpu_break)) |
cmd_halt <= 1'b1; |
if (i_rst) |
cmd_halt <= (START_HALTED == 1)? 1'b1 : 1'b0; |
else if (dbg_cmd_write) |
cmd_halt <= ((i_dbg_data[10])||(i_dbg_data[8])); |
else if ((cmd_step)||(cpu_break)) |
cmd_halt <= 1'b1; |
|
initial cmd_clear_pf_cache = 1'b1; |
initial cmd_clear_pf_cache = 1'b0; |
always @(posedge i_clk) |
cmd_clear_pf_cache <= (dbg_cmd_write)&&(dbg_idata[`CLEAR_CACHE_BIT]); |
if (i_rst) |
cmd_clear_pf_cache <= 1'b0; |
else if (dbg_cmd_write) |
cmd_clear_pf_cache <= i_dbg_data[11]; |
else |
cmd_clear_pf_cache <= 1'b0; |
// |
initial cmd_step = 1'b0; |
always @(posedge i_clk) |
cmd_step <= (dbg_cmd_write)&&(dbg_idata[`STEP_BIT]); |
cmd_step <= (dbg_cmd_write)&&(i_dbg_data[8]); |
// |
initial cmd_addr = 5'h0; |
always @(posedge i_clk) |
if (dbg_cmd_write) |
cmd_addr <= dbg_idata[4:0]; |
cmd_addr <= i_dbg_data[4:0]; |
|
wire cpu_reset; |
assign cpu_reset = (cmd_reset); |
assign cpu_reset = (cmd_reset)||(i_rst); |
|
wire cpu_halt, cpu_dbg_stall; |
assign cpu_halt = (cmd_halt); |
assign cpu_halt = (i_rst)||((cmd_halt)&&(~cmd_step)); |
wire [31:0] cmd_data; |
// Values: |
// 0x0003f -> cmd_addr mask |
// 0x00040 -> reset |
// 0x00080 -> PIC interrrupt pending |
// 0x00080 -> PIC interrrupts enabled |
// 0x00100 -> cmd_step |
// 0x00200 -> cmd_stall |
// 0x00400 -> cmd_halt |
197,17 → 161,19
// 0x10000 -> External interrupt line is high |
assign cmd_data = { 7'h00, 8'h00, i_ext_int, |
cpu_dbg_cc, |
1'b0, cmd_halt, (!cpu_dbg_stall), 1'b0, |
i_ext_int, cpu_reset, 1'b0, cmd_addr }; |
1'b0, cmd_halt, (~cpu_dbg_stall), 1'b0, |
1'b0, cpu_reset, 1'b0, cmd_addr }; |
|
// |
// The CPU itself |
// |
wire cpu_lcl_cyc, cpu_lcl_stb, |
cpu_dbg_we, |
wire cpu_gbl_stb, cpu_lcl_cyc, cpu_lcl_stb, |
cpu_we, cpu_dbg_we, |
cpu_op_stall, cpu_pf_stall, cpu_i_count; |
wire [31:0] cpu_data; |
wire [31:0] cpu_dbg_data; |
assign cpu_dbg_we = ((dbg_stb)&&(dbg_we)&&(dbg_addr)); |
assign cpu_dbg_we = ((i_dbg_cyc)&&(i_dbg_stb) |
&&(i_dbg_we)&&(i_dbg_addr)); |
zipcpu #(.RESET_ADDRESS(RESET_ADDRESS), |
.ADDRESS_WIDTH(ADDRESS_WIDTH), |
.LGICACHE(LGICACHE), |
214,7 → 180,7
.WITH_LOCAL_BUS(0)) |
thecpu(i_clk, cpu_reset, i_ext_int, |
cpu_halt, cmd_clear_pf_cache, cmd_addr[4:0], cpu_dbg_we, |
dbg_idata, cpu_dbg_stall, cpu_dbg_data, |
i_dbg_data, cpu_dbg_stall, cpu_dbg_data, |
cpu_dbg_cc, cpu_break, |
o_wb_cyc, o_wb_stb, |
cpu_lcl_cyc, cpu_lcl_stb, |
223,23 → 189,17
(i_wb_err)||(cpu_lcl_cyc), |
cpu_op_stall, cpu_pf_stall, cpu_i_count |
`ifdef DEBUG_SCOPE |
, o_cpu_debug |
, o_zip_debug |
`endif |
); |
|
// Return debug response values |
assign dbg_odata = (!dbg_addr)?cmd_data :cpu_dbg_data; |
initial dbg_ack = 1'b0; |
assign o_dbg_data = (~i_dbg_addr)?cmd_data :cpu_dbg_data; |
initial o_dbg_ack = 1'b0; |
always @(posedge i_clk) |
dbg_ack <= (dbg_stb)&&(!o_dbg_stall); |
assign dbg_stall= (cpu_dbg_stall)&&(dbg_addr); |
o_dbg_ack <= (i_dbg_cyc)&&((~i_dbg_addr)||(~o_dbg_stall)); |
assign o_dbg_stall=(i_dbg_cyc)&&(cpu_dbg_stall)&&(i_dbg_addr); |
|
assign o_ext_int = (cmd_halt) && (!i_wb_stall); |
assign o_ext_int = (cmd_halt) && (~i_wb_stall); |
|
// Make Verilator happy |
// verilator lint_off UNUSED |
wire [4:0] unused; |
assign unused = { dbg_cyc, cpu_lcl_stb, cpu_op_stall, cpu_pf_stall, cpu_i_count }; |
// verilator lint_on UNUSED |
|
endmodule |
/zipsystem.v
6,9 → 6,11
// |
// Purpose: This portion of the ZIP CPU implements a number of soft |
// peripherals to the CPU nearby its CORE. The functionality |
// sits on the data bus, and does not include any true external hardware |
// peripherals. The peripherals included here include: |
// sits on the data bus, and does not include any true |
// external hardware peripherals. The peripherals included here |
// include: |
// |
// |
// Local interrupt controller--for any/all of the interrupts generated |
// here. This would include a pin for interrupts generated |
// elsewhere, so this interrupt controller could be a master |
19,6 → 21,21
// modern systems (Linux), they tend to send all interrupts to the |
// same interrupt vector anyway. Hence, that's what we do here. |
// |
// Bus Error interrupts -- generates an interrupt any time the wishbone |
// bus produces an error on a given access, for whatever purpose |
// also records the address on the bus at the time of the error. |
// |
// Trap instructions |
// Writing to this "register" will always create an interrupt. |
// After the interrupt, this register may be read to see what |
// value had been written to it. |
// |
// Bit reverse register ... ? |
// |
// (Potentially an eventual floating point co-processor ...) |
// |
// Real-time clock |
// |
// Interval timer(s) (Count down from fixed value, and either stop on |
// zero, or issue an interrupt and restart automatically on zero) |
// These can be implemented as watchdog timers if desired--the |
29,41 → 46,25
// interrupt/time-out line is wired to the reset line instead of |
// the interrupt line of the CPU. |
// |
// Direct Memory Access Controller: This controller allows you to command |
// automatic memory moves. Such memory moves will take place |
// without the CPU's involvement until they are done. See the |
// DMA specification for more information. (Currently contained |
// w/in the ZipCPU spec.) |
// ROM Memory map |
// Set a register to control this map, and a DMA will begin to |
// fill this memory from a slower FLASH. Once filled, accesses |
// will be from this memory instead of |
// |
// (Potentially an eventual floating point co-processor ...?) |
// |
// Busses: The ZipSystem implements a series of busses to make this take |
// place. These busses are identified by their prefix: |
// Doing some market comparison, let's look at what peripherals a TI |
// MSP430 might offer: MSP's may have I2C ports, SPI, UART, DMA, ADC, |
// Comparators, 16,32-bit timers, 16x16 or 32x32 timers, AES, BSL, |
// brown-out-reset(s), real-time-clocks, temperature sensors, USB ports, |
// Spi-Bi-Wire, UART Boot-strap Loader (BSL), programmable digital I/O, |
// watchdog-timers, |
// |
// cpu This is the bus as the CPU sees it. Since the CPU controls |
// two busses (a local and a global one), it uses _gbl_ to indicate |
// the external bus (going through the MMU if necessary) and |
// _lcl_ to indicate a peripheral bus seen here. |
// |
// mmu Sits between the CPU's wishbone interface and the external |
// bus. Has no access to peripherals. |
// |
// sys A local bus implemented here within this space. This is how the |
// CPU talks to the ZipSystem peripherals. However, this bus |
// can also be accessed from the external debug bus. |
// |
// io_dbg |
// io_wb |
// |
// dbg This is identical to the io_dbg bus, but separated by a clock |
// dc The output of the DMA controller |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2019, Gisselquist Technology, LLC |
// Copyright (C) 2015-2017, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
87,20 → 88,23
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`default_nettype none |
// |
`include "cpudefs.v" |
// |
`define RESET_BIT 6 |
`define STEP_BIT 8 |
`define HALT_BIT 10 |
`define CLEAR_CACHE_BIT 11 |
// |
// While I hate adding delays to any bus access, this next delay is required |
// to make timing close in my Basys-3 design. |
`define DELAY_DBG_BUS |
// On my previous version, I needed to add a delay to access the external |
// bus. Activate the define below and that delay will be put back into place. |
// This particular version no longer needs the delay in order to run at |
// 100 MHz. Timing indicates I may even run this at 250 MHz without the |
// delay too, so we're doing better. To get rid of this, I placed the logic |
// determining whether or not I was accessing the local system bus one clock |
// earlier, or into the memops.v file. This also required my wishbone bus |
// arbiter to maintain the bus selection as well, so that got updated ... |
// you get the picture. But, the bottom line is that I no longer need this |
// delay. |
// |
// `define DELAY_EXT_BUS |
// `define DELAY_EXT_BUS // Required no longer! |
// |
// |
// If space is tight, you might not wish to have your performance and |
114,31 → 118,27
// |
// Now, where am I placing all of my peripherals? |
`define PERIPHBASE 32'hc0000000 |
`define INTCTRL 8'h0 // |
`define WATCHDOG 8'h1 // Interrupt generates reset signal |
`define BUSWATCHDOG 8'h2 // Sets IVEC[0] |
`define CTRINT 8'h3 // Sets IVEC[5] |
`define TIMER_A 8'h4 // Sets IVEC[4] |
`define TIMER_B 8'h5 // Sets IVEC[3] |
`define TIMER_C 8'h6 // Sets IVEC[2] |
`define JIFFIES 8'h7 // Sets IVEC[1] |
`define INTCTRL 5'h0 // |
`define WATCHDOG 5'h1 // Interrupt generates reset signal |
`define BUSWATCHDOG 5'h2 // Sets IVEC[0] |
`define CTRINT 5'h3 // Sets IVEC[5] |
`define TIMER_A 5'h4 // Sets IVEC[4] |
`define TIMER_B 5'h5 // Sets IVEC[3] |
`define TIMER_C 5'h6 // Sets IVEC[2] |
`define JIFFIES 5'h7 // Sets IVEC[1] |
|
|
`ifdef INCLUDE_ACCOUNTING_COUNTERS |
`define MSTR_TASK_CTR 8'h08 |
`define MSTR_MSTL_CTR 8'h09 |
`define MSTR_PSTL_CTR 8'h0a |
`define MSTR_INST_CTR 8'h0b |
`define USER_TASK_CTR 8'h0c |
`define USER_MSTL_CTR 8'h0d |
`define USER_PSTL_CTR 8'h0e |
`define USER_INST_CTR 8'h0f |
`define MSTR_TASK_CTR 5'h08 |
`define MSTR_MSTL_CTR 5'h09 |
`define MSTR_PSTL_CTR 5'h0a |
`define MSTR_INST_CTR 5'h0b |
`define USER_TASK_CTR 5'h0c |
`define USER_MSTL_CTR 5'h0d |
`define USER_PSTL_CTR 5'h0e |
`define USER_INST_CTR 5'h0f |
`endif |
|
`ifdef OPT_MMU |
`define MMU_ADDR 8'h80 |
`endif |
|
// Although I have a hole at 5'h2, the DMA controller requires four wishbone |
// addresses, therefore we place it by itself and expand our address bus |
// width here by another bit. |
161,7 → 161,7
// |
// |
// |
module zipsystem(i_clk, i_reset, |
module zipsystem(i_clk, i_rst, |
// Wishbone master interface from the CPU |
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, o_wb_sel, |
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err, |
176,17 → 176,13
, o_cpu_debug |
`endif |
); |
parameter RESET_ADDRESS=32'h1000_0000, ADDRESS_WIDTH=30, |
LGICACHE=10, |
LGDCACHE=12; // Set to zero for no data cache |
parameter [0:0] START_HALTED=1; |
parameter EXTERNAL_INTERRUPTS=1, |
parameter RESET_ADDRESS=32'h0100000, ADDRESS_WIDTH=30, |
LGICACHE=10, START_HALTED=1, EXTERNAL_INTERRUPTS=1, |
`ifdef OPT_MULTIPLY |
IMPLEMENT_MPY = `OPT_MULTIPLY; |
IMPLEMENT_MPY = `OPT_MULTIPLY, |
`else |
IMPLEMENT_MPY = 0; |
IMPLEMENT_MPY = 0, |
`endif |
parameter [0:0] |
`ifdef OPT_DIVIDE |
IMPLEMENT_DIVIDE=1, |
`else |
199,33 → 195,23
`endif |
IMPLEMENT_LOCK=1; |
localparam // Derived parameters |
PHYSICAL_ADDRESS_WIDTH=ADDRESS_WIDTH, |
PAW=ADDRESS_WIDTH, |
`ifdef OPT_MMU |
VIRTUAL_ADDRESS_WIDTH=30, |
`else |
VIRTUAL_ADDRESS_WIDTH=PAW, |
`endif |
LGTLBSZ = 6, |
VAW=VIRTUAL_ADDRESS_WIDTH; |
|
localparam AW=ADDRESS_WIDTH; |
input wire i_clk, i_reset; |
AW=ADDRESS_WIDTH; |
input i_clk, i_rst; |
// Wishbone master |
output wire o_wb_cyc, o_wb_stb, o_wb_we; |
output wire [(PAW-1):0] o_wb_addr; |
output wire [(AW-1):0] o_wb_addr; |
output wire [31:0] o_wb_data; |
output wire [3:0] o_wb_sel; |
input wire i_wb_ack, i_wb_stall; |
input wire [31:0] i_wb_data; |
input wire i_wb_err; |
input i_wb_ack, i_wb_stall; |
input [31:0] i_wb_data; |
input i_wb_err; |
// Incoming interrupts |
input wire [(EXTERNAL_INTERRUPTS-1):0] i_ext_int; |
input [(EXTERNAL_INTERRUPTS-1):0] i_ext_int; |
// Outgoing interrupt |
output wire o_ext_int; |
// Wishbone slave |
input wire i_dbg_cyc, i_dbg_stb, i_dbg_we, i_dbg_addr; |
input wire [31:0] i_dbg_data; |
input i_dbg_cyc, i_dbg_stb, i_dbg_we, i_dbg_addr; |
input [31:0] i_dbg_data; |
output wire o_dbg_ack; |
output wire o_dbg_stall; |
output wire [31:0] o_dbg_data; |
263,19 → 249,11
`endif |
else |
`ifdef INCLUDE_ACCOUNTING_COUNTERS |
if (EXTERNAL_INTERRUPTS >= 15) |
assign alt_int_vector = { i_ext_int[14:8], |
mtc_int, moc_int, mpc_int, mic_int, |
utc_int, uoc_int, upc_int, uic_int }; |
else |
assign alt_int_vector = { {(7-(EXTERNAL_INTERRUPTS-9)){1'b0}}, |
i_ext_int[(EXTERNAL_INTERRUPTS-1):9], |
mtc_int, moc_int, mpc_int, mic_int, |
utc_int, uoc_int, upc_int, uic_int }; |
`else |
if (EXTERNAL_INTERRUPTS >= 24) |
assign alt_int_vector = { i_ext_int[(EXTERNAL_INTERRUPTS-1):9] }; |
else |
assign alt_int_vector = { {(15-(EXTERNAL_INTERRUPTS-9)){1'b0}}, |
i_ext_int[(EXTERNAL_INTERRUPTS-1):9] }; |
`endif |
286,15 → 264,11
wire dbg_cyc, dbg_stb, dbg_we, dbg_addr, dbg_stall; |
wire [31:0] dbg_idata, dbg_odata; |
reg dbg_ack; |
`ifdef DELAY_DBG_BUS |
wire dbg_err, no_dbg_err; |
wire [3:0] dbg_sel; |
wire no_dbg_err; |
`ifdef DELAY_DBG_BUS |
// Make verilator happy |
// verilator lint_off UNUSED |
// verilator lint_on UNUSED |
wire dbg_err; |
assign dbg_err = 1'b0; |
busdelay #(1,32) wbdelay(i_clk, i_reset, |
busdelay #(1,32) wbdelay(i_clk, |
i_dbg_cyc, i_dbg_stb, i_dbg_we, i_dbg_addr, i_dbg_data, 4'hf, |
o_dbg_ack, o_dbg_stall, o_dbg_data, no_dbg_err, |
dbg_cyc, dbg_stb, dbg_we, dbg_addr, dbg_idata, dbg_sel, |
308,8 → 282,6
assign o_dbg_ack = dbg_ack; |
assign o_dbg_stall = dbg_stall; |
assign o_dbg_data = dbg_odata; |
assign dbg_sel = 4'b1111; |
assign no_dbg_err = 1'b0; |
`endif |
|
// |
316,26 → 288,12
// |
// |
wire sys_cyc, sys_stb, sys_we; |
wire [7:0] sys_addr; |
wire [(PAW-1):0] cpu_addr; |
wire [4:0] sys_addr; |
wire [(AW-1):0] cpu_addr; |
wire [31:0] sys_data; |
reg [31:0] sys_idata; |
reg sys_ack; |
wire sys_stall; |
wire sys_ack, sys_stall; |
|
wire sel_counter, sel_timer, sel_pic, sel_apic, |
sel_watchdog, sel_bus_watchdog, sel_dmac, sel_mmus; |
// |
assign sel_pic = (sys_stb)&&(sys_addr == `INTCTRL); |
assign sel_watchdog = (sys_stb)&&(sys_addr == `WATCHDOG); |
assign sel_bus_watchdog= (sys_stb)&&(sys_addr == `BUSWATCHDOG); |
assign sel_apic = (sys_stb)&&(sys_addr == `CTRINT); |
assign sel_timer = (sys_stb)&&(sys_addr[7:2] == 6'h1); |
assign sel_counter = (sys_stb)&&(sys_addr[7:3] == 5'h1); |
assign sel_dmac = (sys_stb)&&(sys_addr[7:4] == 4'h1); |
assign sel_mmus = (sys_stb)&&(sys_addr[7]); |
|
// |
// The external debug interface |
// |
// We offer only a limited interface here, requiring a pre-register |
352,44 → 310,39
reg cmd_reset, cmd_halt, cmd_step, cmd_clear_pf_cache; |
reg [5:0] cmd_addr; |
wire [3:0] cpu_dbg_cc; |
assign dbg_cmd_write = (dbg_stb)&&(dbg_we)&&(!dbg_addr); |
assign dbg_cmd_write = (dbg_cyc)&&(dbg_stb)&&(dbg_we)&&(~dbg_addr); |
// |
// Always start us off with an initial reset |
// |
initial cmd_reset = 1'b1; |
always @(posedge i_clk) |
cmd_reset <= ((dbg_cmd_write)&&(dbg_idata[`RESET_BIT])) |
||(wdt_reset); |
cmd_reset <= ((dbg_cmd_write)&&(dbg_idata[6])); |
// |
initial cmd_halt = START_HALTED; |
always @(posedge i_clk) |
if (i_reset) |
cmd_halt <= START_HALTED; |
else if (cmd_reset) |
cmd_halt <= START_HALTED; |
else if (dbg_cmd_write) |
cmd_halt <= ((dbg_idata[`HALT_BIT])&&(!dbg_idata[`STEP_BIT])); |
else if ((cmd_step)||(cpu_break)) |
cmd_halt <= 1'b1; |
if (i_rst) |
cmd_halt <= (START_HALTED == 1)? 1'b1 : 1'b0; |
else if (dbg_cmd_write) |
cmd_halt <= ((dbg_idata[10])||(dbg_idata[8])); |
else if ((cmd_step)||(cpu_break)) |
cmd_halt <= 1'b1; |
|
initial cmd_clear_pf_cache = 1'b1; |
always @(posedge i_clk) |
cmd_clear_pf_cache <= (dbg_cmd_write)&&(dbg_idata[`CLEAR_CACHE_BIT]); |
cmd_clear_pf_cache = (~i_rst)&&(dbg_cmd_write) |
&&((dbg_idata[11])||(dbg_idata[6])); |
// |
initial cmd_step = 1'b0; |
always @(posedge i_clk) |
cmd_step <= (dbg_cmd_write)&&(dbg_idata[`STEP_BIT]); |
cmd_step <= (dbg_cmd_write)&&(dbg_idata[8]); |
// |
initial cmd_addr = 6'h0; |
always @(posedge i_clk) |
if (dbg_cmd_write) |
cmd_addr <= dbg_idata[5:0]; |
|
wire cpu_reset; |
assign cpu_reset = (cmd_reset); |
assign cpu_reset = (cmd_reset)||(wdt_reset)||(i_rst); |
|
wire cpu_halt, cpu_dbg_stall; |
assign cpu_halt = (cmd_halt); |
assign cpu_halt = (i_rst)||((cmd_halt)&&(~cmd_step)); |
wire [31:0] pic_data; |
wire [31:0] cmd_data; |
// Values: |
409,11 → 362,11
assign cmd_data = { {(16-EXTERNAL_INTERRUPTS){1'b0}}, |
i_ext_int, |
cpu_dbg_cc, // 4 bits |
1'b0, cmd_halt, (!cpu_dbg_stall), 1'b0, |
1'b0, cmd_halt, (~cpu_dbg_stall), 1'b0, |
pic_data[15], cpu_reset, cmd_addr }; |
else |
assign cmd_data = { i_ext_int[15:0], cpu_dbg_cc, |
1'b0, cmd_halt, (!cpu_dbg_stall), 1'b0, |
1'b0, cmd_halt, (~cpu_dbg_stall), 1'b0, |
pic_data[15], cpu_reset, cmd_addr }; |
endgenerate |
|
426,8 → 379,8
wire wdt_ack, wdt_stall, wdt_reset; |
wire [31:0] wdt_data; |
ziptimer #(32,31,0) |
watchdog(i_clk, cpu_reset, !cmd_halt, |
sys_cyc, (sys_stb)&&(sel_watchdog), sys_we, |
watchdog(i_clk, cpu_reset, ~cmd_halt, |
sys_cyc, ((sys_stb)&&(sys_addr == `WATCHDOG)), sys_we, |
sys_data, |
wdt_ack, wdt_stall, wdt_data, wdt_reset); |
|
438,20 → 391,21
// take less than the number written to this register. |
// |
reg wdbus_ack; |
reg [(PAW-1):0] r_wdbus_data; |
reg [(AW-1):0] r_wdbus_data; |
wire [31:0] wdbus_data; |
wire [14:0] wdbus_ignored_data; |
wire reset_wdbus_timer, wdbus_int; |
assign reset_wdbus_timer = (!o_wb_cyc)||(o_wb_stb)||(i_wb_ack); |
assign reset_wdbus_timer = ((o_wb_cyc)&&((o_wb_stb)||(i_wb_ack))); |
wbwatchdog #(14) watchbus(i_clk,(cpu_reset)||(reset_wdbus_timer), |
14'h2000, wdbus_int); |
o_wb_cyc, 14'h2000, wdbus_int); |
initial r_wdbus_data = 0; |
always @(posedge i_clk) |
if ((wdbus_int)||(cpu_err)) |
r_wdbus_data <= o_wb_addr; |
assign wdbus_data = { {(32-PAW){1'b0}}, r_wdbus_data }; |
if ((wdbus_int)||(cpu_ext_err)) |
r_wdbus_data = o_wb_addr; |
assign wdbus_data = { {(32-AW){1'b0}}, r_wdbus_data }; |
initial wdbus_ack = 1'b0; |
always @(posedge i_clk) |
wdbus_ack <= ((sys_cyc)&&(sys_stb)&&(sel_bus_watchdog)); |
wdbus_ack <= ((sys_cyc)&&(sys_stb)&&(sys_addr == 5'h02)); |
|
// Counters -- for performance measurement and accounting |
// |
467,8 → 421,8
// Master task counter |
wire mtc_ack, mtc_stall; |
wire [31:0] mtc_data; |
zipcounter mtask_ctr(i_clk, 1'b0, (!cpu_halt), sys_cyc, |
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b000), |
zipcounter mtask_ctr(i_clk, (~cpu_halt), sys_cyc, |
(sys_stb)&&(sys_addr == `MSTR_TASK_CTR), |
sys_we, sys_data, |
mtc_ack, mtc_stall, mtc_data, mtc_int); |
|
475,8 → 429,8
// Master Operand Stall counter |
wire moc_ack, moc_stall; |
wire [31:0] moc_data; |
zipcounter mmstall_ctr(i_clk,1'b0, (cpu_op_stall), sys_cyc, |
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b001), |
zipcounter mmstall_ctr(i_clk,(cpu_op_stall), sys_cyc, |
(sys_stb)&&(sys_addr == `MSTR_MSTL_CTR), |
sys_we, sys_data, |
moc_ack, moc_stall, moc_data, moc_int); |
|
483,8 → 437,8
// Master PreFetch-Stall counter |
wire mpc_ack, mpc_stall; |
wire [31:0] mpc_data; |
zipcounter mpstall_ctr(i_clk,1'b0, (cpu_pf_stall), sys_cyc, |
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b010), |
zipcounter mpstall_ctr(i_clk,(cpu_pf_stall), sys_cyc, |
(sys_stb)&&(sys_addr == `MSTR_PSTL_CTR), |
sys_we, sys_data, |
mpc_ack, mpc_stall, mpc_data, mpc_int); |
|
491,8 → 445,8
// Master Instruction counter |
wire mic_ack, mic_stall; |
wire [31:0] mic_data; |
zipcounter mins_ctr(i_clk,1'b0, (cpu_i_count), sys_cyc, |
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b011), |
zipcounter mins_ctr(i_clk,(cpu_i_count), sys_cyc, |
(sys_stb)&&(sys_addr == `MSTR_INST_CTR), |
sys_we, sys_data, |
mic_ack, mic_stall, mic_data, mic_int); |
|
503,8 → 457,8
// User task counter |
wire utc_ack, utc_stall; |
wire [31:0] utc_data; |
zipcounter utask_ctr(i_clk,1'b0, (!cpu_halt)&&(cpu_gie), sys_cyc, |
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b100), |
zipcounter utask_ctr(i_clk,(~cpu_halt)&&(cpu_gie), sys_cyc, |
(sys_stb)&&(sys_addr == `USER_TASK_CTR), |
sys_we, sys_data, |
utc_ack, utc_stall, utc_data, utc_int); |
|
511,8 → 465,8
// User Op-Stall counter |
wire uoc_ack, uoc_stall; |
wire [31:0] uoc_data; |
zipcounter umstall_ctr(i_clk,1'b0, (cpu_op_stall)&&(cpu_gie), sys_cyc, |
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b101), |
zipcounter umstall_ctr(i_clk,(cpu_op_stall)&&(cpu_gie), sys_cyc, |
(sys_stb)&&(sys_addr == `USER_MSTL_CTR), |
sys_we, sys_data, |
uoc_ack, uoc_stall, uoc_data, uoc_int); |
|
519,8 → 473,8
// User PreFetch-Stall counter |
wire upc_ack, upc_stall; |
wire [31:0] upc_data; |
zipcounter upstall_ctr(i_clk,1'b0, (cpu_pf_stall)&&(cpu_gie), sys_cyc, |
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b110), |
zipcounter upstall_ctr(i_clk,(cpu_pf_stall)&&(cpu_gie), sys_cyc, |
(sys_stb)&&(sys_addr == `USER_PSTL_CTR), |
sys_we, sys_data, |
upc_ack, upc_stall, upc_data, upc_int); |
|
527,8 → 481,8
// User instruction counter |
wire uic_ack, uic_stall; |
wire [31:0] uic_data; |
zipcounter uins_ctr(i_clk,1'b0, (cpu_i_count)&&(cpu_gie), sys_cyc, |
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b111), |
zipcounter uins_ctr(i_clk,(cpu_i_count)&&(cpu_gie), sys_cyc, |
(sys_stb)&&(sys_addr == `USER_INST_CTR), |
sys_we, sys_data, |
uic_ack, uic_stall, uic_data, uic_int); |
|
564,7 → 518,7
assign uic_int = 1'b0; |
|
always @(posedge i_clk) |
actr_ack <= sel_counter; |
actr_ack <= (sys_stb)&&(sys_addr[4:3] == 2'b01); |
`endif // INCLUDE_ACCOUNTING_COUNTERS |
|
// |
575,14 → 529,14
wire dmac_ack, dmac_stall; |
wire dc_cyc, dc_stb, dc_we, dc_ack, dc_stall; |
wire [31:0] dc_data; |
wire [(PAW-1):0] dc_addr; |
wire [(AW-1):0] dc_addr; |
wire cpu_gbl_cyc; |
wire [31:0] dmac_int_vec; |
assign dmac_int_vec = { 1'b0, alt_int_vector, 1'b0, |
main_int_vector[14:1], 1'b0 }; |
assign dmac_stb = (sys_stb)&&(sel_dmac); |
assign dmac_stb = (sys_stb)&&(sys_addr[4]); |
`ifdef INCLUDE_DMA_CONTROLLER |
wbdmac #(PAW) dma_controller(i_clk, cpu_reset, |
wbdmac #(AW) dma_controller(i_clk, cpu_reset, |
sys_cyc, dmac_stb, sys_we, |
sys_addr[1:0], sys_data, |
dmac_ack, dmac_stall, dmac_data, |
595,7 → 549,6
dmac_int); |
`else |
reg r_dmac_ack; |
initial r_dmac_ack = 1'b0; |
always @(posedge i_clk) |
r_dmac_ack <= (sys_cyc)&&(dmac_stb); |
assign dmac_ack = r_dmac_ack; |
605,7 → 558,7
assign dc_cyc = 1'b0; |
assign dc_stb = 1'b0; |
assign dc_we = 1'b0; |
assign dc_addr = { (PAW) {1'b0} }; |
assign dc_addr = { (AW) {1'b0} }; |
assign dc_data = 32'h00; |
|
assign dmac_int = 1'b0; |
614,21 → 567,21
wire ctri_sel, ctri_stall; |
reg ctri_ack; |
wire [31:0] ctri_data; |
assign ctri_sel = (sys_stb)&&(sel_apic); |
initial ctri_ack = 1'b0; |
assign ctri_sel = (sys_stb)&&(sys_addr == `CTRINT); |
always @(posedge i_clk) |
ctri_ack <= (ctri_sel)&&(!cpu_reset); |
ctri_ack <= ctri_sel; |
assign ctri_stall = 1'b0; |
`ifdef INCLUDE_ACCOUNTING_COUNTERS |
// |
// Counter Interrupt controller |
// |
generate if (EXTERNAL_INTERRUPTS <= 9) |
begin : ALT_PIC |
generate |
if (EXTERNAL_INTERRUPTS <= 9) |
begin |
icontrol #(8) ctri(i_clk, cpu_reset, (ctri_sel), |
sys_data, ctri_data, alt_int_vector[7:0], |
ctri_int); |
end else begin : ALT_PIC |
end else begin |
icontrol #(8+(EXTERNAL_INTERRUPTS-9)) |
ctri(i_clk, cpu_reset, (ctri_sel), |
sys_data, ctri_data, |
638,12 → 591,13
|
`else // INCLUDE_ACCOUNTING_COUNTERS |
|
generate if (EXTERNAL_INTERRUPTS <= 9) |
begin : ALT_PIC |
generate |
if (EXTERNAL_INTERRUPTS <= 9) |
begin |
assign ctri_stall = 1'b0; |
assign ctri_data = 32'h0000; |
assign ctri_int = 1'b0; |
end else begin : ALT_PIC |
end else begin |
icontrol #(EXTERNAL_INTERRUPTS-9) |
ctri(i_clk, cpu_reset, (ctri_sel), |
sys_data, ctri_data, |
658,10 → 612,10
// |
wire tma_ack, tma_stall; |
wire [31:0] tma_data; |
ziptimer timer_a(i_clk, cpu_reset, !cmd_halt, |
sys_cyc, (sys_stb)&&(sel_timer)&&(sys_addr[1:0] == 2'b00), |
sys_we, sys_data, |
tma_ack, tma_stall, tma_data, tma_int); |
ziptimer timer_a(i_clk, cpu_reset, ~cmd_halt, |
sys_cyc, (sys_stb)&&(sys_addr == `TIMER_A), sys_we, |
sys_data, |
tma_ack, tma_stall, tma_data, tma_int); |
|
// |
// Timer B |
668,10 → 622,10
// |
wire tmb_ack, tmb_stall; |
wire [31:0] tmb_data; |
ziptimer timer_b(i_clk, cpu_reset, !cmd_halt, |
sys_cyc, (sys_stb)&&(sel_timer)&&(sys_addr[1:0] == 2'b01), |
sys_we, sys_data, |
tmb_ack, tmb_stall, tmb_data, tmb_int); |
ziptimer timer_b(i_clk, cpu_reset, ~cmd_halt, |
sys_cyc, (sys_stb)&&(sys_addr == `TIMER_B), sys_we, |
sys_data, |
tmb_ack, tmb_stall, tmb_data, tmb_int); |
|
// |
// Timer C |
678,10 → 632,10
// |
wire tmc_ack, tmc_stall; |
wire [31:0] tmc_data; |
ziptimer timer_c(i_clk, cpu_reset, !cmd_halt, |
sys_cyc, (sys_stb)&&(sel_timer)&&(sys_addr[1:0]==2'b10), |
sys_we, sys_data, |
tmc_ack, tmc_stall, tmc_data, tmc_int); |
ziptimer timer_c(i_clk, cpu_reset, ~cmd_halt, |
sys_cyc, (sys_stb)&&(sys_addr == `TIMER_C), sys_we, |
sys_data, |
tmc_ack, tmc_stall, tmc_data, tmc_int); |
|
// |
// JIFFIES |
688,8 → 642,8
// |
wire jif_ack, jif_stall; |
wire [31:0] jif_data; |
zipjiffies jiffies(i_clk, cpu_reset, !cmd_halt, |
sys_cyc, (sys_stb)&&(sel_timer)&&(sys_addr[1:0] == 2'b11), sys_we, |
zipjiffies jiffies(i_clk, ~cmd_halt, |
sys_cyc, (sys_stb)&&(sys_addr == `JIFFIES), sys_we, |
sys_data, |
jif_ack, jif_stall, jif_data, jif_int); |
|
697,17 → 651,18
// The programmable interrupt controller peripheral |
// |
wire pic_interrupt; |
generate if (EXTERNAL_INTERRUPTS < 9) |
begin : MAIN_PIC |
generate |
if (EXTERNAL_INTERRUPTS < 9) |
begin |
icontrol #(6+EXTERNAL_INTERRUPTS) pic(i_clk, cpu_reset, |
(sys_cyc)&&(sys_stb)&&(sys_we) |
&&(sel_pic), |
&&(sys_addr==`INTCTRL), |
sys_data, pic_data, |
main_int_vector[(6+EXTERNAL_INTERRUPTS-1):0], pic_interrupt); |
end else begin : MAIN_PIC |
end else begin |
icontrol #(15) pic(i_clk, cpu_reset, |
(sys_cyc)&&(sys_stb)&&(sys_we) |
&&(sel_pic), |
&&(sys_addr==`INTCTRL), |
sys_data, pic_data, |
main_int_vector[14:0], pic_interrupt); |
end endgenerate |
716,7 → 671,7
assign pic_stall = 1'b0; |
reg pic_ack; |
always @(posedge i_clk) |
pic_ack <= (sys_stb)&&(sel_pic); |
pic_ack <= (sys_stb)&&(sys_addr == `INTCTRL); |
|
// |
// The CPU itself |
723,21 → 678,20
// |
wire cpu_gbl_stb, cpu_lcl_cyc, cpu_lcl_stb, |
cpu_we, cpu_dbg_we; |
wire [31:0] cpu_data, cpu_idata; |
wire [3:0] cpu_sel, mmu_sel; |
wire [31:0] cpu_data, wb_data; |
wire [3:0] cpu_sel; |
wire cpu_ack, cpu_stall, cpu_err; |
wire [31:0] cpu_dbg_data; |
assign cpu_dbg_we = ((dbg_cyc)&&(dbg_stb)&&(!cmd_addr[5]) |
assign cpu_dbg_we = ((dbg_cyc)&&(dbg_stb)&&(~cmd_addr[5]) |
&&(dbg_we)&&(dbg_addr)); |
zipcpu #( .RESET_ADDRESS(RESET_ADDRESS), |
.ADDRESS_WIDTH(VIRTUAL_ADDRESS_WIDTH), |
zipcpu #( |
.RESET_ADDRESS(RESET_ADDRESS), |
.ADDRESS_WIDTH(ADDRESS_WIDTH), |
.LGICACHE(LGICACHE), |
.OPT_LGDCACHE(LGDCACHE), |
.IMPLEMENT_MPY(IMPLEMENT_MPY), |
.IMPLEMENT_DIVIDE(IMPLEMENT_DIVIDE), |
.IMPLEMENT_FPU(IMPLEMENT_FPU), |
.IMPLEMENT_LOCK(IMPLEMENT_LOCK), |
.WITH_LOCAL_BUS(1'b1) |
.IMPLEMENT_LOCK(IMPLEMENT_LOCK) |
) |
thecpu(i_clk, cpu_reset, pic_interrupt, |
cpu_halt, cmd_clear_pf_cache, cmd_addr[4:0], cpu_dbg_we, |
746,8 → 700,8
cpu_gbl_cyc, cpu_gbl_stb, |
cpu_lcl_cyc, cpu_lcl_stb, |
cpu_we, cpu_addr, cpu_data, cpu_sel, |
// Return values from the Wishbone bus |
cpu_ack, cpu_stall, cpu_idata, cpu_err, |
cpu_ack, cpu_stall, wb_data, |
cpu_err, |
cpu_op_stall, cpu_pf_stall, cpu_i_count |
`ifdef DEBUG_SCOPE |
, o_cpu_debug |
754,112 → 708,24
`endif |
); |
|
wire ext_ack, ext_stall; |
wire mmu_cyc, mmu_stb, mmu_we, mmu_stall, mmu_ack, mmu_err; |
wire mmus_ack, mmus_stall; |
wire [PAW-1:0] mmu_addr; |
wire [31:0] mmu_data, mmu_idata, mmus_data; |
|
// Specific responses from the MMU |
wire cpu_miss; |
|
// The mmu_cpu_ lines are the return bus lines from the MMU. They |
// are separate from the cpu_'s lines simply because either the sys_ |
// (local) bus or the mmu_cpu_ (global) bus might return a response to |
// the CPU, and the responses haven't been merged back together again |
// yet. |
wire mmu_cpu_ack, mmu_cpu_stall; |
wire [31:0] mmu_cpu_idata; |
|
// The wires associated with cache snooping |
wire pf_return_stb, pf_return_we, pf_return_cachable; |
wire [19:0] pf_return_v, pf_return_p; |
|
`ifdef OPT_MMU |
// Ok ... here's the MMU |
zipmmu #( .LGTBL(LGTLBSZ), |
.ADDRESS_WIDTH(PHYSICAL_ADDRESS_WIDTH) |
) |
themmu(i_clk, cpu_reset, |
// Slave interface |
(sys_stb)&&(sel_mmus), |
sys_we, sys_addr[7:0], sys_data, |
mmus_ack, mmus_stall, mmus_data, |
// CPU global bus master lines |
cpu_gbl_cyc, cpu_gbl_stb, cpu_we, cpu_addr, |
cpu_data, cpu_sel, |
// MMU bus master outgoing lines |
mmu_cyc, mmu_stb, mmu_we, mmu_addr, mmu_data, mmu_sel, |
// .... and the return from the slave(s) |
mmu_stall, mmu_ack, mmu_err, mmu_idata, |
// CPU gobal bus master return lines |
mmu_cpu_stall, mmu_cpu_ack, cpu_err, cpu_miss, mmu_cpu_idata, |
pf_return_stb, pf_return_we, pf_return_p, pf_return_v, |
pf_return_cachable); |
|
`else |
assign mmu_cyc = cpu_gbl_cyc; |
assign mmu_stb = cpu_gbl_stb; |
assign mmu_we = cpu_we; |
assign mmu_addr = cpu_addr; |
assign mmu_data = cpu_data; |
assign mmu_sel = cpu_sel; |
assign cpu_miss = 1'b0; |
assign cpu_err = (mmu_err)&&(cpu_gbl_cyc); |
assign mmu_cpu_idata = mmu_idata; |
assign mmu_cpu_stall = mmu_stall; |
assign mmu_cpu_ack = mmu_ack; |
reg r_mmus_ack; |
initial r_mmus_ack = 1'b0; |
always @(posedge i_clk) |
r_mmus_ack <= (sys_stb)&&(sys_addr[7]); |
assign mmus_ack = r_mmus_ack; |
assign mmus_stall = 1'b0; |
assign mmus_data = 32'h0; |
|
assign pf_return_stb = 0; |
assign pf_return_v = 0; |
assign pf_return_p = 0; |
assign pf_return_we = 0; |
assign pf_return_cachable = 0; |
`endif |
// Responses from the MMU still need to be merged/muxed back together |
// with the responses from the local bus |
assign cpu_ack = ((cpu_lcl_cyc)&&(sys_ack)) |
||((cpu_gbl_cyc)&&(mmu_cpu_ack)); |
assign cpu_stall = ((cpu_lcl_cyc)&&(sys_stall)) |
||((cpu_gbl_cyc)&&(mmu_cpu_stall)); |
assign cpu_idata = (cpu_gbl_cyc)?mmu_cpu_idata : sys_idata; |
|
// The following lines (will be/) are used to allow the prefetch to |
// snoop on any external interaction. Until this capability is |
// integrated into the CPU, they are unused. Here we tell Verilator |
// not to be surprised that these lines are unused: |
|
// verilator lint_off UNUSED |
wire [(3+1+20+20-1):0] mmu_unused; |
assign mmu_unused = { pf_return_stb, pf_return_we, |
pf_return_p, pf_return_v, pf_return_cachable, |
cpu_miss }; |
// verilator lint_on UNUSED |
|
// Now, arbitrate the bus ... first for the local peripherals |
// For the debugger to have access to the local system bus, the |
// following must be true: |
// (dbg_cyc) The debugger must request the bus |
// (!cpu_lcl_cyc) The CPU cannot be using it (CPU gets priority) |
// (~cpu_lcl_cyc) The CPU cannot be using it (CPU gets priority) |
// (dbg_addr) The debugger must be requesting its data |
// register, not just the control register |
// and one of two other things. Either |
// ((cpu_halt)&&(!cpu_dbg_stall)) the CPU is completely halted, |
// ((cpu_halt)&&(~cpu_dbg_stall)) the CPU is completely halted, |
// or |
// (!cmd_addr[5]) we are trying to read a CPU register |
// (~cmd_addr[5]) we are trying to read a CPU register |
// while in motion. Let the user beware that, |
// by not waiting for the CPU to fully halt, |
// his results may not be what he expects. |
// |
wire sys_dbg_cyc = ((dbg_cyc)&&(!cpu_lcl_cyc)&&(dbg_addr)) |
&&(cmd_addr[5]); |
wire sys_dbg_cyc = ((dbg_cyc)&&(~cpu_lcl_cyc)&&(dbg_addr)) |
&&(((cpu_halt)&&(~cpu_dbg_stall)) |
||(~cmd_addr[5])); |
assign sys_cyc = (cpu_lcl_cyc)||(sys_dbg_cyc); |
assign sys_stb = (cpu_lcl_cyc) |
? (cpu_lcl_stb) |
866,23 → 732,16
: ((dbg_stb)&&(dbg_addr)&&(cmd_addr[5])); |
|
assign sys_we = (cpu_lcl_cyc) ? cpu_we : dbg_we; |
assign sys_addr= (cpu_lcl_cyc) ? cpu_addr[7:0] : { 3'h0, cmd_addr[4:0]}; |
assign sys_addr= (cpu_lcl_cyc) ? cpu_addr[4:0] : cmd_addr[4:0]; |
assign sys_data= (cpu_lcl_cyc) ? cpu_data : dbg_idata; |
|
// Return debug response values |
// A return from one of three busses: |
// CMD giving command instructions to the CPU (step, halt, etc) |
// CPU-DBG-DATA internal register responses from within the CPU |
// sys Responses from the front-side bus here in the ZipSystem |
assign dbg_odata = (!dbg_addr) ? cmd_data |
:((!cmd_addr[5])?cpu_dbg_data : sys_idata); |
assign dbg_odata = (~dbg_addr)?cmd_data |
:((~cmd_addr[5])?cpu_dbg_data : wb_data); |
initial dbg_ack = 1'b0; |
always @(posedge i_clk) |
dbg_ack <= (dbg_stb)&&(!dbg_stall); |
assign dbg_stall=(dbg_cyc)&&( |
((!sys_dbg_cyc)&&(cpu_dbg_stall)) |
||(sys_stall) |
)&&(dbg_addr); |
dbg_ack <= (dbg_cyc)&&(dbg_stb)&&(~dbg_stall); |
assign dbg_stall=(dbg_cyc)&&((~sys_dbg_cyc)||(sys_stall))&&(dbg_addr); |
|
// Now for the external wishbone bus |
// Need to arbitrate between the flash cache and the CPU |
890,32 → 749,22
// cache gets access to the bus--the CPU will be stuck until the |
// flash cache is finished with the bus. |
wire ext_cyc, ext_stb, ext_we, ext_err; |
wire [(PAW-1):0] ext_addr; |
wire cpu_ext_ack, cpu_ext_stall, ext_ack, ext_stall, |
cpu_ext_err; |
wire [(AW-1):0] ext_addr; |
wire [31:0] ext_odata; |
wire [3:0] ext_sel; |
wbpriarbiter #(32,PAW) dmacvcpu(i_clk, |
mmu_cyc, mmu_stb, mmu_we, mmu_addr, mmu_data, mmu_sel, |
mmu_ack, mmu_stall, mmu_err, |
wbpriarbiter #(32,AW) dmacvcpu(i_clk, |
cpu_gbl_cyc, cpu_gbl_stb, cpu_we, cpu_addr, cpu_data, cpu_sel, |
cpu_ext_ack, cpu_ext_stall, cpu_ext_err, |
dc_cyc, dc_stb, dc_we, dc_addr, dc_data, 4'hf, |
dc_ack, dc_stall, dc_err, |
ext_cyc, ext_stb, ext_we, ext_addr, ext_odata, ext_sel, |
ext_ack, ext_stall, ext_err); |
assign mmu_idata = ext_idata; |
/* |
assign ext_cyc = mmu_cyc; |
assign ext_stb = mmu_stb; |
assign ext_we = mmu_we; |
assign ext_odata= mmu_data; |
assign ext_addr = mmu_addr; |
assign ext_sel = mmu_sel; |
assign mmu_ack = ext_ack; |
assign mmu_stall= ext_stall; |
assign mmu_err = ext_err; |
*/ |
|
`ifdef DELAY_EXT_BUS |
busdelay #(.AW(PAW),.DW(32),.DELAY_STALL(0)) extbus(i_clk, i_reset, |
ext_cyc, ext_stb, ext_we, ext_addr, ext_odata, ext_sel, |
busdelay #(AW,32) extbus(i_clk, |
ext_cyc, ext_stb, ext_we, ext_addr, ext_odata, |
ext_ack, ext_stall, ext_idata, ext_err, |
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, o_wb_sel, |
i_wb_ack, i_wb_stall, i_wb_data, (i_wb_err)||(wdbus_int)); |
939,48 → 788,19
:(tmb_ack ? tmb_data |
:(tmc_ack ? tmc_data |
:jif_data)); |
always @(posedge i_clk) |
casez({ mmus_ack, tmr_ack, wdt_ack, actr_ack, |
dmac_ack, pic_ack, ctri_ack, wdbus_ack }) |
8'b1???????: sys_idata <= mmus_data; |
8'b01??????: sys_idata <= tmr_data; |
8'b001?????: sys_idata <= wdt_data; |
8'b0001????: sys_idata <= actr_data; |
8'b00001???: sys_idata <= dmac_data; |
8'b000001??: sys_idata <= pic_data; |
8'b0000001?: sys_idata <= ctri_data; |
8'b00000001: sys_idata <= wdbus_data; |
default: sys_idata <= mmus_data; |
endcase |
assign wb_data = (tmr_ack|wdt_ack)?((tmr_ack)?tmr_data:wdt_data) |
:((actr_ack|dmac_ack)?((actr_ack)?actr_data:dmac_data) |
:((pic_ack|ctri_ack)?((pic_ack)?pic_data:ctri_data) |
:((wdbus_ack)?wdbus_data:(ext_idata)))); |
|
always @(posedge i_clk) |
if ((i_reset)||(!sys_cyc)) |
sys_ack <= 1'b0; |
else |
sys_ack <= (|{ mmu_ack, tmr_ack, wdt_ack, actr_ack, |
dmac_ack, pic_ack, ctri_ack, wdbus_ack, |
mmus_ack }); |
|
assign sys_stall = (tma_stall | tmb_stall | tmc_stall | jif_stall |
| wdt_stall | ctri_stall | actr_stall |
| pic_stall | dmac_stall | mmus_stall); // Always 1'b0! |
| pic_stall | dmac_stall); // Always 1'b0! |
assign cpu_stall = (sys_stall)|(cpu_ext_stall); |
assign sys_ack = (tmr_ack|wdt_ack|ctri_ack|actr_ack|pic_ack|dmac_ack|wdbus_ack); |
assign cpu_ack = (sys_ack)||(cpu_ext_ack); |
assign cpu_err = (cpu_ext_err)&&(cpu_gbl_cyc); |
|
assign o_ext_int = (cmd_halt) && (!cpu_stall); |
assign o_ext_int = (cmd_halt) && (~cpu_stall); |
|
// Make verilator happy |
// verilator lint_off UNUSED |
wire [5:0] unused; |
assign unused = { no_dbg_err, dbg_sel, sel_mmus }; |
`ifndef INCLUDE_ACCOUNTING_COUNTERS |
wire [11:0] unused_ctrs; |
assign unused_ctrs = { |
moc_int, mpc_int, mic_int, mtc_int, |
uoc_int, upc_int, uic_int, utc_int, |
cpu_gie, cpu_op_stall, cpu_pf_stall, cpu_i_count }; |
`endif |
`ifndef INCLUDE_DMA_CONTROLLER |
wire [34:0] unused_dmac; |
assign unused_dmac = { dc_err, dc_ack, dc_stall, dmac_int_vec }; |
`endif |
// verilator lint_on UNUSED |
endmodule |