OpenCores
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 205 to Rev 209
    Reverse comparison

Rev 205 → Rev 209

/Makefile
1,42 → 1,48
################################################################################
#
# 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.
#
# 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.
##
## 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
## <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
##
##
################################################################################
#
##
##
.PHONY: all
all: zipsystem zipbones cpudefs.h div zipmmu cpuops pfcache
 
CORED:= core
PRPHD:= peripherals
AUXD := aux
EXD := ex
VSRC := zipsystem.v cpudefs.v \
$(PRPHD)/wbdmac.v $(PRPHD)/icontrol.v \
$(PRPHD)/zipcounter.v $(PRPHD)/zipjiffies.v \
46,8 → 52,8
$(CORED)/pfcache.v \
$(CORED)/memops.v $(CORED)/pipemem.v \
$(CORED)/div.v \
$(AUXD)/busdelay.v \
$(AUXD)/wbdblpriarb.v $(AUXD)/wbpriarbiter.v \
$(EXD)/busdelay.v \
$(EXD)/wbdblpriarb.v $(EXD)/wbpriarbiter.v \
$(CORED)/idecode.v $(CORED)/cpuops.v
VZIP := zipbones.v cpudefs.v \
$(CORED)/zipcpu.v $(CORED)/cpuops.v $(CORED)/idecode.v \
55,13 → 61,20
$(CORED)/pfcache.v \
$(CORED)/memops.v $(CORED)/pipemem.v \
$(CORED)/div.v \
$(AUXD)/busdelay.v $(AUXD)/wbdblpriarb.v \
$(EXD)/busdelay.v $(EXD)/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 --trace -cc -y $(CORED) -y $(PRPHD) -y $(AUXD)
VERILATE=$(VERILATOR) $(VFLAGS)
 
$(VOBJ)/Vzipsystem.cpp: $(VSRC)
$(VERILATE) zipsystem.v
137,3 → 150,12
.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
/README.md
0,0 → 1,30
This directory contains three sub-directories:
 
- [core](core), where all of the actual components to the CPU proper are contained
- [peripherals](peripherals), where several common CPU peripherals are kept. These aren't really external peripherals per se, although they may be implemented as such. Rather, these peripherals are components that are important to the CPU's functionality. As such, they are often distributed with the CPU proper, and used internally by supervisor programs.
- [ex](ex), where some general wishbone cores are kept, such as arbiters, delays, and even where I keep a copy of the formal wishbone properties.
 
Within this ZipCPU RTL directory are just the two primary wrappers for the
ZipCPU: [ZipBones](zipbones.v) and [ZipSystem](zipsystem.v). These wrappers
connect an external wishbone slave interface to the debugging port of the
CPU, so that the CPU can be reset, started, stopped, and in general debugged.
 
The [ZipBones](./zipbones.v) would be the appropriate wrapper if you want the
CPU to fit in the tightest space possible (Ex: Digilent's [CMod
S6](https://github.com/ZipCPU/s6soc)).
 
Use the [ZipSystem](./zipsystem.v) if you want to give the CPU some
basic peripherals, such as:
 
- 2x [Interrupt controllers](./peripherals/icontrol.v)
- [Timers](peripherals/ziptimer), and an [experimental timer called zipjiffies](peripherals/zipjiffies.v) that's been with the CPU for some time
- [Performance counters](peripherals/zipcounter.v), to measure your performance
- A [Direct Memory Access Controller](./peripherals/wbdmac.v)
- Or even the [Memory Management Unit](./peripherals/zipmmu.v)
 
If you are just looking for the CPU's code itself, check out
[zipcpu.v](core/zipcpu.v)
within the
[core](core) subdirectory.
That's of the main core of the ZipCPU itself.
 
/core/README.md
0,0 → 1,49
## The Core of the ZipCPU
 
Here are the core files to the ZipCPU. In here, you'll find not only the
[main ZipCPU core](./zipcpu.v), but also:
 
- Several prefetch routines
 
o [prefetch.v](./prefetch.v) an older prefetch module that only fetched
one instruction at a time, and so prevented pipelining
 
o [pipefetch.v](./pipefetch.v), my first attempt at building a prefetch with
cache. It took a rather unique approach to the cache, implementing it as
a rolling window in memory. This file really sticks around for historical
reasons, but not much more.
 
o [dblfetch.v](./dbgfetch.v), fetches two instructions at once (on subsequent
clocks). This is designed to increase the speed of the CPU when it isn't
pipelined, by exploiting the fact that many memory accesses go faster for
the second access.
 
o [pfcache.v](./pfcache.v), this is the current/best instruction cache
for the CPU.
 
 
- [idecode.v](./idecode.v), an instruction decoder
 
- Several memory access routines
 
o [memops.v](./memops.v), a typical/traditional one memory operation at a
time means of accessing memory. This was my first approach to memory,
and the appropriate approach still when the CPU is not running in its
pipelind mode.
 
o [pipemem.v](./pipemem.v), a faster memory access method that groups
consecutive memory accesses together into a pipelined bus access.
This routine has so far compensated for the fact that the ZipCPU does not
(yet) have an integrated data cache.
 
o [dcache.v](./dcache.v), is my attempt at building a data cache. This
has never been integrated with the CPU, and may not be integrated until
the MMU is also integrated.
 
- [div.v](./div.v), the divide unit
 
- [cpuops.v](./cpuops.v), the ALU unit
 
The defines within [cpudefs.v](../cpudefs.v) will determine which of these
modules gets linked into your CPU.
 
/core/cpuops.v
4,9 → 4,8
//
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core
//
// 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).
// Purpose: This is the ZipCPU ALU function. It handles all of the
// instruction opcodes 0-13. (14-15 are divide opcodes).
//
//
// Creator: Dan Gisselquist, Ph.D.
14,7 → 13,7
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2017, Gisselquist Technology, LLC
// Copyright (C) 2015-2019, 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
37,37 → 36,53
//
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
//
`include "cpudefs.v"
//
module cpuops(i_clk,i_rst, i_ce, i_op, i_a, i_b, o_c, o_f, o_valid,
module cpuops(i_clk,i_reset, i_stb, i_op, i_a, i_b, o_c, o_f, o_valid,
o_busy);
parameter IMPLEMENT_MPY = `OPT_MULTIPLY;
input i_clk, i_rst, i_ce;
input [3:0] i_op;
input [31:0] i_a, i_b;
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;
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;
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
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]}
 
: ( { 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
78,241 → 93,48
wire z, n, v;
reg c, pre_sign, set_ovfl, keep_sgn_on_ovfl;
always @(posedge i_clk)
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
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
 
always @(posedge i_clk)
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
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
 
wire [63:0] mpy_result; // Where we dump the multiply result
reg mpyhi; // Return the high half of the multiply
wire 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_ce)&&((i_op[3:1]==3'h5)||(i_op[3:0]==4'hc));
assign this_is_a_multiply_op = (i_stb)&&((i_op[3:1]==3'h5)||(i_op[3:0]==4'hc));
 
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;
//
// Pull in the multiply logic from elsewhere
//
`ifdef FORMAL
`define MPYOP abs_mpy
`else
 
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;
`define MPYOP mpyop
`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_ce)
if (i_stb)
begin
pre_sign <= (i_a[31]);
c <= 1'b0;
333,16 → 155,19
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_rst)
r_busy <= 1'b0;
else
r_busy <= ((IMPLEMENT_MPY > 1)
&&(this_is_a_multiply_op))||mpybusy;
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;
 
assign o_busy = (r_busy); // ||((IMPLEMENT_MPY>1)&&(this_is_a_multiply_op));
 
 
355,11 → 180,108
 
initial o_valid = 1'b0;
always @(posedge i_clk)
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);
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);
 
`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,35 → 6,24
//
// Purpose: This is one step beyond the simplest instruction fetch,
// prefetch.v. dblfetch.v uses memory pipelining to fetch two
// 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.
// (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.
//
// 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.
// 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).
//
// 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).
// 20180222: Completely rebuilt.
//
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2017, Gisselquist Technology, LLC
// Copyright (C) 2017-2019, 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
58,27 → 47,29
////////////////////////////////////////////////////////////////////////////////
//
//
module dblfetch(i_clk, i_rst, i_new_pc, i_clear_cache,
i_stall_n, i_pc, o_i, o_pc, o_v,
`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,
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=32, AUX_WIDTH = 1;
localparam AW=ADDRESS_WIDTH;
input i_clk, i_rst, i_new_pc, i_clear_cache,
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,
i_stall_n;
input [(AW-1):0] i_pc;
output reg [31:0] o_i;
output reg [(AW-1):0] o_pc;
output wire o_v;
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;
// 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 [31:0] o_wb_data;
output wire [(DW-1):0] o_wb_data;
// And return inputs
input i_wb_ack, i_wb_stall, i_wb_err;
input [31:0] i_wb_data;
input wire i_wb_ack, i_wb_stall, i_wb_err;
input wire [(DW-1):0] i_wb_data;
// And ... the result if we got an error
output reg o_illegal;
 
85,87 → 76,67
assign o_wb_we = 1'b0;
assign o_wb_data = 32'h0000;
 
reg last_ack, last_stb, invalid_bus_cycle;
reg last_stb, invalid_bus_cycle;
 
reg [31:0] cache [0:1];
reg cache_read_addr, cache_write_addr;
reg [1:0] cache_valid;
reg [(DW-1):0] cache_word;
reg cache_valid;
reg [1:0] inflight;
reg cache_illegal;
 
initial o_wb_cyc = 1'b0;
initial o_wb_stb = 1'b0;
always @(posedge i_clk)
if ((i_rst)||(i_wb_err))
if ((i_reset)||((o_wb_cyc)&&(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))
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;
if ((!o_wb_stb)||(!i_wb_stall))
o_wb_stb <= (!last_stb);
 
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)))
))
// 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)))
begin
o_wb_cyc <= 1'b0;
o_wb_stb <= 1'b0;
end
 
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
end else if ((i_new_pc)||(invalid_bus_cycle)
||((o_valid)&&(i_stall_n)&&(!o_illegal)))
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 last_stb = 1'b0;
initial inflight = 2'b00;
always @(posedge i_clk)
if ((o_wb_cyc)&&(o_wb_stb)&&(!i_wb_stall))
last_stb <= 1'b1;
else if (!o_wb_cyc)
last_stb <= 1'b0;
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
 
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;
always @(*)
last_stb = (inflight != 2'b00)||((o_valid)&&(!i_stall_n));
 
initial invalid_bus_cycle = 1'b0;
always @(posedge i_clk)
if (i_rst)
invalid_bus_cycle <= 1'b0;
else if ((i_new_pc)||(i_clear_cache))
if ((o_wb_cyc)&&(i_new_pc))
invalid_bus_cycle <= 1'b1;
else if (!o_wb_cyc)
invalid_bus_cycle <= 1'b0;
173,60 → 144,573
initial o_wb_addr = {(AW){1'b1}};
always @(posedge i_clk)
if (i_new_pc)
o_wb_addr <= i_pc;
else if ((o_wb_stb)&&(!i_wb_stall)&&(!invalid_bus_cycle))
o_wb_addr <= i_pc[AW+1:2];
else if ((o_wb_stb)&&(!i_wb_stall))
o_wb_addr <= o_wb_addr + 1'b1;
 
initial cache_write_addr = 1'b0;
//////////////////
//
// Now for the immediate output word to the CPU
//
//////////////////
 
initial o_valid = 1'b0;
always @(posedge i_clk)
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;
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;
 
always @(posedge i_clk)
if ((o_wb_cyc)&&(i_wb_ack))
cache[cache_write_addr] <= i_wb_data;
if ((!o_valid)||(i_stall_n))
begin
if (cache_valid)
o_insn <= cache_word;
else
o_insn <= i_wb_data;
end
 
initial cache_read_addr = 1'b0;
initial o_pc[1:0] = 2'b00;
always @(posedge i_clk)
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;
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;
 
initial o_illegal = 1'b0;
always @(posedge i_clk)
if ((i_new_pc)||(invalid_bus_cycle))
cache_valid <= 2'b00;
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;
else begin
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;
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;
end
 
initial o_i = {(32){1'b1}};
always @(posedge i_clk)
if ((i_stall_n)&&(o_wb_cyc)&&(i_wb_ack))
o_i <= i_wb_data;
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;
else
o_i <= cache[cache_read_addr];
// Otherwise, count the clocks the CPU takes to respond
f_cpu_delay <= f_cpu_delay + 1'b1;
 
initial o_pc = 0;
always @(posedge i_clk)
if (i_new_pc)
o_pc <= i_pc;
else if ((o_v)&&(i_stall_n))
o_pc <= o_pc + 1'b1;
assume(f_cpu_delay < F_CPU_DELAY);
`endif
 
assign o_v = cache_valid[cache_read_addr];
 
initial o_illegal = 1'b0;
 
/////////////////////////////////////////////////
//
//
// Assertions about our outputs
//
//
/////////////////////////////////////////////////
always @(posedge i_clk)
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;
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);
 
//
// 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/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, Gisselquist Technology, LLC
// Copyright (C) 2016-2019, 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,88 → 67,146
////////////////////////////////////////////////////////////////////////////////
//
//
module dcache(i_clk, i_rst, i_pipe_stb, i_lock,
`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,
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,
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data);
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 LGCACHELEN = 8,
ADDRESS_WIDTH=32,
LGNLINES=5, // Log of the number of separate cache lines
IMPLEMENT_LOCK=0,
ADDRESS_WIDTH=30,
LGNLINES=(LGCACHELEN-3), // Log of the number of separate cache lines
NAUX=5; // # of aux d-wires to keep aligned w/memops
localparam SDRAM_BIT = 26;
localparam FLASH_BIT = 22;
localparam BLKRAM_BIT= 15;
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 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
localparam LGAUX = 3; // log_2 of the maximum number of piped data
input i_clk, i_rst;
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;
// Interface from the CPU
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
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
// Outputs, going back to the CPU
output wire o_busy, o_pipe_stalled, o_valid, o_err;
output reg o_busy;
output reg o_pipe_stalled;
output reg o_valid, o_err;
output reg [(NAUX-1):0] o_wreg;
output reg [31:0] o_data;
output reg [(DW-1):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 [31:0] o_wb_data;
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;
// Wishbone bus slave response inputs
input i_wb_ack, i_wb_stall, i_wb_err;
input [31:0] i_wb_data;
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;
 
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 [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;
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;
 
 
// 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 [31:0] c_wdata;
reg [(DW-1):0] c_wdata;
reg [(DW/8-1):0] c_wsel;
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;
 
assign i_cline = i_addr[(CS-1):LS];
assign i_caddr = i_addr[(CS-1):0];
assign i_ctag = i_addr[(AW-1):LS];
`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+2];
assign i_caddr = i_addr[(CS+1):2];
 
wire cache_miss_inow, w_cachable;
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])));
assign cache_miss_inow = (!last_tag_valid)
||(last_tag != i_addr[(AW+1):LS+2])
||(!c_v[i_cline]);
 
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 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;
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];
155,9 → 213,14
assign r_ctag = r_addr[(AW-1):LS];
 
 
reg wr_cstb, r_iv, pipeable_op, non_pipeable_op, in_cache;
reg wr_cstb, r_iv, 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.
//
165,15 → 228,28
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_op)&&(!cache_miss_inow)&&(w_cachable)
&&(i_pipe_stb)&&(!c_wr)&&(!wr_cstb);
r_svalid<= (i_pipe_stb)&&(!i_op[0])&&(w_cachable)
&&(!cache_miss_inow)&&(!c_wr)&&(!wr_cstb);
 
//
// The two clock in-cache path
180,25 → 256,37
//
// Some preliminaries that needed to be calculated on the first
// clock
if (!o_busy)
if ((!o_pipe_stalled)&&(!r_rd_pending))
r_addr <= i_addr[(AW+1):2];
if ((!o_pipe_stalled)&&(!r_rd_pending))
begin
r_iv <= c_v[i_cline];
r_itag <= c_vtags[i_cline];
r_addr <= i_addr;
r_cachable <= (!i_op)&&(w_cachable)&&(i_pipe_stb);
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));
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_idata still contains the right answer
r_rd <= (i_pipe_stb)&&(!i_op);
r_ddata <= r_idata;
r_rd <= (i_pipe_stb)&&(!i_op[0]);
// 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_itag == r_ctag)&&(r_iv)&&(r_cachable);
if ((r_itag == r_ctag)&&(r_iv)&&(r_cachable))
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;
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.
214,63 → 302,453
// Two clock path -- misses as well
&&(r_rd)&&(!r_svalid)
&&((r_itag != r_ctag)||(!r_iv));
end
 
r_rdata <= c_mem[r_addr[(CS-1):0]];
r_rvalid<= ((i_wb_ack)&&(last_ack));
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
end
 
`define DC_IDLE 2'b00
`define DC_WRITE 2'b01
`define DC_READS 2'b10
`define DC_READC 2'b11
reg [1:0] state;
assign o_wb_sel = (state == DC_READC) ? 4'hf : r_sel;
 
reg [(AW-LS-1):0] wr_wtag, wr_vtag;
reg [31:0] wr_data;
reg [(CS-1):0] wr_addr;
initial o_wb_data = 0;
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_wdata <= wr_data;
c_waddr <= wr_addr[(CS-1):0];
// c_wr <= (wr_cstb)&&(wr_wtag == wr_vtag);
// c_waddr <= wr_addr[(CS-1):0];
c_wr <= 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 <= 1)
if (LS <= 0)
end_of_line <= 1'b1;
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})));
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
 
if (LS <= 1)
if (!cyc)
last_line_stb <= (LS <= 0);
else if ((stb)&&(!i_wb_stall)&&(LS <= 1))
last_line_stb <= 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})));
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}});
 
//
if (state == `DC_IDLE)
pipeable_op <= 1'b0;
if (state == `DC_IDLE)
non_pipeable_op <= 1'b0;
 
 
if (state == `DC_IDLE)
//
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;
280,47 → 758,58
o_wb_stb_gbl <= 1'b0;
o_wb_stb_lcl <= 1'b0;
 
in_cache <= (i_op)&&(w_cachable);
if ((i_pipe_stb)&&(i_op))
in_cache <= (i_op[0])&&(w_cachable);
if ((i_pipe_stb)&&(i_op[0]))
begin // Write operation
state <= `DC_WRITE;
o_wb_addr <= i_addr;
state <= DC_WRITE;
o_wb_addr <= i_addr[(AW+1):2];
o_wb_we <= 1'b1;
pipeable_op <= 1'b1;
 
cyc <= 1'b1;
stb <= 1'b1;
 
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);
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
 
end else if (r_cache_miss)
begin
state <= `DC_READC;
o_wb_addr <= { i_ctag, {(LS){1'b0}} };
non_pipeable_op <= 1'b1;
state <= DC_READC;
o_wb_addr <= { r_ctag, {(LS){1'b0}} };
 
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;
pipeable_op <= 1'b1;
state <= DC_READS;
o_wb_addr <= i_addr[(AW+1):2];
 
cyc <= 1'b1;
stb <= 1'b1;
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);
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
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.
331,26 → 820,31
o_wb_addr[(LS-1):0] <= o_wb_addr[(LS-1):0]+1'b1;
end
 
if(stb)
c_v[o_wb_addr[(CS-LS-1):0]] <= 1'b0;
if ((i_wb_ack)&&(!end_of_line))
c_v[o_wb_addr[(CS-1):LS]] <= 1'b0;
 
c_wr <= (i_wb_ack);
c_wdata <= o_wb_data;
c_waddr <= ((c_wr)?(c_waddr+1'b1):c_waddr);
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_vtags[o_wb_addr[(CS-LS-1):0]]<= o_wb_addr[(AW-LS-1):0];
set_vflag <= !i_wb_err;
if (i_wb_ack)
c_vtags[r_addr[(CS-1):LS]]
<= r_addr[(AW-1):LS];
 
if (((i_wb_ack)&&(end_of_line))||(i_wb_err))
begin
state <= `DC_IDLE;
non_pipeable_op <= 1'b0;
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;
//
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
359,27 → 853,31
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_data;
o_wb_addr <= i_addr[(AW+1):2];
 
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 <= (c_v[])&&(c_tag[])&&(in_cache)&&(stb);
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_wdata <= o_wb_data;
c_waddr <= (state == `DC_IDLE)?i_caddr
: ((c_wr)?(c_waddr+1'b1):c_waddr);
c_waddr <= r_addr[CS-1:0];
c_wsel <= o_wb_sel;
 
if ((!i_wb_stall)&&(!i_pipe_stb))
begin
386,27 → 884,79
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)&&(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))
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))
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
413,10 → 963,19
// 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)
c_mem[c_waddr] <= c_wdata;
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
 
//
// Reads from the cache
426,82 → 985,1101
// going to be our output will need to be determined with combinatorial
// logic on the output.
//
reg [31:0] cached_idata, cached_rdata;
always @(posedge i_clk)
cached_idata <= c_mem[i_caddr];
generate if (OPT_DUAL_READ_PORT)
begin
 
always @(posedge i_clk)
cached_rdata <= c_mem[r_caddr];
always @(posedge i_clk)
cached_idata <= c_mem[i_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 @(posedge i_clk)
always @(*)
if (r_svalid)
o_data <= cached_idata;
else if ((i_wb_ack)&&(pipeable_op))
o_data <= i_wb_data;
pre_data = cached_idata;
else if (state == DC_READS)
pre_data = i_wb_data;
else
o_data <= cached_rdata;
pre_data = cached_rdata;
 
always @(posedge i_clk)
o_valid <= (r_svalid)||((i_wb_ack)&&(pipeable_op))
||(r_dvalid)||(r_rvalid);
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;
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);
 
assign o_busy = (state != `DC_IDLE);
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;
 
//
// 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;
 
////////////////////////////////////////////////
//
// Handle our auxilliary data lines.
// Reset properties
//
// 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.
//
// 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.
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
 
////////////////////////////////////////////////
//
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;
// Assumptions about our inputs
//
////////////////////////////////////////////////
//
//
always @(*)
if (o_pipe_stalled)
`ASSUME(!i_pipe_stb);
 
always @(*)
if (!f_past_valid)
`ASSUME(!i_pipe_stb);
 
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))
&&($past(i_pipe_stb))&&($past(o_pipe_stalled)))
begin
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;
`ASSUME($stable(i_pipe_stb));
`ASSUME($stable(i_op[0]));
`ASSUME($stable(i_addr));
if (i_op[0])
`ASSUME($stable(i_data));
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
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];
// 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));
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
////////////////////////////////////////////////
//
// We can use our FIFO addresses to pre-calculate when an ACK is going
// to be the last_noncachable_ack.
// 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;
 
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 o_pipe_stalled=((pipeable_op)&&(i_wb_stall))||(non_pipeable_op);
// pipeable_op must become zero when stb goes low
assign f_cval_in_cache= (c_v[f_const_addr[CS-1:LS]])
&&(f_ctag_here == f_const_tag);
 
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
lock_gbl <= (i_lock)&&((r_wb_cyc_gbl)||(lock_gbl));
lock_lcl <= (i_lock)&&((r_wb_cyc_lcl)||(lock_lcl));
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]));
end
 
assign o_wb_cyc_gbl = (r_wb_cyc_gbl)||(lock_gbl);
assign o_wb_cyc_lcl = (r_wb_cyc_lcl)||(lock_lcl);
 
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
endmodule
/core/div.v
8,10 → 8,10
// for both signed and unsigned divide.
//
// Steps:
// i_rst The DIVide unit starts in idle. It can also be placed into an
// i_reset The DIVide unit starts in idle. It can also be placed into an
// idle by asserting the reset input.
//
// i_wr When i_rst is asserted, a divide begins. On the next clock:
// i_wr When i_reset 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-2017, Gisselquist Technology, LLC
// Copyright (C) 2015-2019, 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,15 → 95,17
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
// `include "cpudefs.v"
//
module div(i_clk, i_rst, i_wr, i_signed, i_numerator, i_denominator,
module div(i_clk, i_reset, i_wr, i_signed, i_numerator, i_denominator,
o_busy, o_valid, o_err, o_quotient, o_flags);
parameter BW=32, LGBW = 5;
input i_clk, i_rst;
input wire i_clk, i_reset;
// Input parameters
input i_wr, i_signed;
input [(BW-1):0] i_numerator, i_denominator;
input wire i_wr, i_signed;
input wire [(BW-1):0] i_numerator, i_denominator;
// Output parameters
output reg o_busy, o_valid, o_err;
output reg [(BW-1):0] o_quotient;
113,11 → 115,10
// before we are valid, so it can't be o_busy ...
//
reg r_busy;
reg [(2*BW-2):0] r_divisor;
reg [(BW-1):0] r_dividend;
reg [BW-1:0] r_divisor;
reg [(2*BW-2):0] r_dividend;
wire [(BW):0] diff; // , xdiff[(BW-1):0];
assign diff = r_dividend - r_divisor[(BW-1):0];
// assign xdiff= r_dividend - { 1'b0, r_divisor[(BW-1):1] };
assign diff = r_dividend[2*BW-2:BW-1] - r_divisor;
 
reg r_sign, pre_sign, r_z, r_c, last_bit;
reg [(LGBW-1):0] r_bit;
130,12 → 131,12
// or equivalently when we discover we are dividing by zero.
initial r_busy = 1'b0;
always @(posedge i_clk)
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;
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;
 
// 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
145,32 → 146,18
// identical.
initial o_busy = 1'b0;
always @(posedge i_clk)
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 (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 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)
// 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;
if (i_wr)
zero_divisor <= (i_denominator == 0);
 
// 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.
183,17 → 170,19
// it on an i_wr signal.
initial o_valid = 1'b0;
always @(posedge i_clk)
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;
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;
 
// 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
200,12 → 189,12
// everything clears.
initial o_err = 1'b0;
always @(posedge i_clk)
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;
if (i_reset)
o_err <= 1'b0;
else if ((r_busy)&&(zero_divisor))
o_err <= 1'b1;
else
o_err <= 1'b0;
 
// r_bit
//
213,26 → 202,31
// 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 ((r_busy)&&(!pre_sign))
r_bit <= r_bit + {(LGBW){1'b1}};
else
r_bit <= {(LGBW){1'b1}};
if (i_reset)
r_bit <= 0;
else if ((r_busy)&&(!pre_sign))
r_bit <= r_bit + 1'b1;
else
r_bit <= 0;
 
// 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 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
// 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
// when (r_bit == 0).
initial last_bit = 1'b0;
always @(posedge i_clk)
if (r_busy)
last_bit <= (r_bit == {{(LGBW-1){1'b0}},1'b1});
else
last_bit <= 1'b0;
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;
 
// pre_sign
//
241,67 → 235,59
// be true for the one clock, and then it must clear itself.
initial pre_sign = 1'b0;
always @(posedge i_clk)
if (i_wr)
pre_sign <= i_signed;
else
pre_sign <= 1'b0;
if (i_reset)
pre_sign <= 1'b0;
else
pre_sign <= (i_wr)&&(i_signed)&&((i_numerator[BW-1])||(i_denominator[BW-1]));
 
// 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. Well ... not quite. If we need to flip the sign of
// our value, then we can't quite clear the zero flag ... yet.
// other cases.
always @(posedge i_clk)
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;
if (i_wr)
r_z <= 1'b1;
else if ((r_busy)&&(!pre_sign)&&(!diff[BW]))
r_z <= 1'b0;
 
// 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 shifted as appropriate for every output bit we are
// looking for--just as with traditional long division.
// the divisor for every output bit we are looking for--just as with
// traditional long division.
always @(posedge i_clk)
if (pre_sign)
if (pre_sign)
begin
// If we are doing a signed divide, then take the
// absolute value of the dividend
if (r_dividend[BW-1])
begin
// 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;
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 };
 
initial r_divisor = 0;
always @(posedge i_clk)
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}} };
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;
 
// r_sign
// is a flag for our state machine control(s). r_sign will be set to
314,31 → 300,38
// up to something.
initial r_sign = 1'b0;
always @(posedge i_clk)
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;
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;
 
initial o_quotient = 0;
always @(posedge i_clk)
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;
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;
 
// 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)
r_c <= (r_busy)&&((diff == 0)||(r_dividend == 0));
if (i_reset)
r_c <= 1'b0;
else
r_c <= (r_busy)&&(diff == 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
349,4 → 342,215
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-2017, Gisselquist Technology, LLC
// Copyright (C) 2015-2019, 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,41 → 43,60
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
`define CPU_SP_REG 4'hd
`define CPU_CC_REG 4'he
`define CPU_PC_REG 4'hf
//
`include "cpudefs.v"
`define CISBIT 31
`define CISIMMSEL 23
`define IMMSEL 18
//
//
//
module idecode(i_clk, i_rst, i_ce, i_stalled,
module idecode(i_clk, i_reset, i_ce, i_stalled,
i_instruction, i_gie, i_pc, i_pf_valid,
i_illegal,
o_valid,
o_phase, o_illegal,
o_pc, o_gie,
o_dcdR, o_dcdA, o_dcdB, o_I, o_zI,
o_pc,
o_dcdR, o_dcdA, o_dcdB,
o_preA, o_preB,
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_early_branch, o_early_branch_stb, 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, 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;
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;
output wire o_valid, o_phase;
output reg o_illegal;
output reg [AW:0] o_pc;
output reg o_gie;
output reg [(AW+1):0] o_pc;
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;
84,30 → 103,24
output reg o_wF;
output reg [3:0] o_op;
output reg o_ALU, o_M, o_DV, o_FP, o_break;
output wire o_lock;
output reg o_lock;
output reg o_wR, o_rA, o_rB;
output wire o_early_branch;
output wire [(AW-1):0] o_branch_pc;
output wire o_early_branch, o_early_branch_stb;
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 */;
 
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;
`ifdef FORMAL
output reg [31:0] f_insn_word;
output reg f_insn_gie;
`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_noop, w_lock, w_sim, w_break, w_special, w_add,
w_mpy;
wire [4:0] w_dcdR, w_dcdB, w_dcdA;
wire w_dcdR_pc, w_dcdR_cc;
wire w_dcdA_pc, w_dcdA_cc;
117,91 → 130,103
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);
 
`ifdef OPT_CIS
reg [15:0] r_nxt_half;
assign iword = (o_phase)
 
reg [14:0] r_nxt_half;
 
generate if (OPT_CIS)
begin : SET_IWORD
 
assign iword = (o_phase)
// set second half as a NOOP ... but really
// shouldn't matter
? { r_nxt_half[15:0], i_instruction[15:0] }
? { 1'b1, r_nxt_half[14:0], i_instruction[15:0] }
: i_instruction;
`else
assign iword = { 1'b0, i_instruction[30:0] };
`endif
end else begin : CLR_IWORD
assign iword = { 1'b0, i_instruction[30:0] };
 
// 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 (EARLY_BRANCHING != 0)
if (OPT_EARLY_BRANCHING)
begin
`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;
if (OPT_CIS)
begin : CIS_EARLY_BRANCHING
 
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_cis_ljmp = (o_phase)&&(iword[31:16] == 16'hfcf8);
 
end else begin : NOCIS_EARLY_BRANCH
 
assign w_cis_ljmp = 1'b0;
 
end
 
assign w_ljmp = (iword == 32'h7c87c000);
end else begin
 
end else begin : NO_EARLY_BRANCHING
 
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;
always @(iword,w_op)
if (!iword[31])
w_cis_op <= w_op;
 
generate if (OPT_CIS)
begin : GEN_CIS_OP
 
always @(*)
if (!iword[`CISBIT])
w_cis_op = iword[26:22];
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;
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
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'h8);
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_cmptst = (w_cis_op[4:1] == 4'h8);
assign w_ldilo = (w_cis_op[4:0] == 5'h9);
assign w_ldilo = (w_cis_op[4:0] == 5'h09);
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)
212,25 → 237,14
// moves in iword[18] but only for the supervisor, and the other
// four bits encoded in the instruction.
//
assign w_dcdR = { ((!iword[31])&&(w_mov)&&(~i_gie))?iword[18]:i_gie,
assign w_dcdR = { ((!iword[`CISBIT])&&(!OPT_NO_USERMODE)&&(w_mov)&&(!i_gie))?iword[`IMMSEL]: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[31])&&(w_mov)&&(~i_gie))?iword[13]:i_gie;
assign w_dcdB[3:0]= (iword[31])
? (((!iword[23])&&(iword[26:25]==2'b10))
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))
? `CPU_SP_REG : iword[22:19])
: iword[17:14];
 
246,26 → 260,18
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.
//
// 3+4 LUTs
assign w_cond = ((w_ldi)||(iword[31])) ? 4'h8 :
// 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 :
{ (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)&&(w_cis_op[4:1] != 4'hf))
((w_fpu)&&(OPT_FPU))
// Divide's read A
||(w_div)
// ALU ops read A,
278,25 → 284,24
// 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[31])&&(iword[18])&&(!w_ldi))
||(( iword[31])&&(iword[23])&&(!w_ldi))
||((!iword[`CISBIT])&&(iword[`IMMSEL])&&(!w_ldi)&&(!w_special))
||(( iword[`CISBIT])&&(iword[`CISIMMSEL])&&(!w_ldi))
// If using compressed instruction sets,
// we *always* read on memory operands.
||(( iword[31])&&(w_mem));
||(( iword[`CISBIT])&&(w_mem));
 
// wR -- will we be writing our result back?
// wR_n = !wR
// 1 LUT: All but STO, NOOP/BREAK/LOCK, and CMP/TST write back to w_dcdR
// All but STO, NOOP/BREAK/LOCK, and CMP/TST write back to w_dcdR
assign w_wR_n = (w_sto)
||((!iword[31])&&(w_cis_op[4:3]==2'b11)
&&(w_cis_op[2:1]!=2'b00)
&&(w_dcdR[3:1]==3'h7))
||(w_special)
||(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)||(w_div)
||((w_cond[3])&&(((w_fpu)&&(OPT_FPU))||(w_div)
||((w_ALU)&&(!w_mov)&&(!w_ldilo)&&(!w_brev)
&&(w_dcdR[3:1] != 3'h7))));
 
313,27 → 318,33
// MOVE immediates have one less bit
:((w_mov) ?{ {(23-13){iword[12]}}, iword[12:0] }
// Normal Op-B immediate ... 18 or 14 bits
:((~iword[18]) ? { {(23-18){iword[17]}}, iword[17:0] }
:((!iword[`IMMSEL]) ? { {(23-18){iword[17]}}, iword[17:0] }
: { {(23-14){iword[13]}}, iword[13:0] }
));
 
`ifdef OPT_CIS
wire [7:0] w_halfbits;
assign w_halfbits = iword[23:16];
generate if (OPT_CIS)
begin : GEN_CIS_IMMEDIATE
wire [7:0] w_halfbits;
assign w_halfbits = iword[`CISIMMSEL:16];
 
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
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
 
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
341,213 → 352,276
// 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.
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))
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))
r_phase <= 1'b0;
else if ((i_ce)&&(i_pf_valid))
r_phase <= (o_phase)? 1'b0
: ((i_instruction[31])&&(i_pf_valid));
else if (i_ce)
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)
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;
`else
assign o_phase = 1'b0;
`endif
assign o_phase = r_phase;
end else begin
assign o_phase = 1'b0;
end endgenerate
 
 
initial o_illegal = 1'b0;
always @(posedge i_clk)
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 (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 ((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_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_FPU==0)&&(w_fpu))
o_illegal <= 1'b1;
if ((!OPT_FPU)&&(w_fpu))
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
if ((!OPT_SIM)&&(w_sim))
// Simulation instructions on real hardware should
// always cause an illegal instruction error
o_illegal <= 1'b1;
 
// 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)
if ((i_ce)&&((o_phase)||(i_pf_valid)))
begin
o_pc[0] <= 1'b0;
 
if (OPT_CIS)
begin
`ifdef OPT_CIS
if (!o_phase)
o_gie<= i_gie;
 
if (iword[31])
if (iword[`CISBIT])
begin
if (o_phase)
o_pc <= o_pc + 1'b1;
else if (i_pf_valid)
o_pc <= { i_pc, 1'b1 };
o_pc[AW+1:1] <= o_pc[AW+1:1] + 1'b1;
else
o_pc <= { i_pc[AW+1:2], 1'b1, 1'b0 };
end else begin
// The normal, non-CIS case
o_pc <= { i_pc + 1'b1, 1'b0 };
o_pc <= { i_pc[AW+1:2] + 1'b1, 2'b00 };
end
`else
o_gie<= i_gie;
o_pc <= { i_pc + 1'b1, 1'b0 };
`endif
end else begin
// The normal, non-CIS case
o_pc <= { i_pc[AW+1:2] + 1'b1, 2'b00 };
end
end
 
// 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;
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;
 
// 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];
// 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;
 
// 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);
o_M <= w_mem;
o_DV <= w_div;
o_FP <= 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)
||((!OPT_LOCK)&&(w_lock));
o_M <= w_mem;
o_DV <= (OPT_DIVIDE)&&(w_div);
o_FP <= (OPT_FPU)&&(w_fpu);
 
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
o_break <= w_break;
o_lock <= (OPT_LOCK)&&(w_lock);
 
`ifdef VERILATOR
if (OPT_CIS)
r_nxt_half <= { iword[14:0] };
else
r_nxt_half <= 0;
 
if (OPT_SIM)
begin
// Support the SIM instruction(s)
o_sim <= (!iword[31])&&(w_op[4:1] == 4'hf)
&&(w_dcdR[3:1] == 3'h7);
`else
o_sim <= (w_sim)||(w_noop);
o_sim_immv <= iword[22:0];
end else begin
o_sim <= 1'b0;
`endif
o_sim_immv <= iword[22:0];
o_sim_immv <= 0;
end
end
 
`ifdef OPT_PIPELINED
assign o_lock = r_lock;
`else
assign o_lock = 1'b0;
`endif
assign o_preA = w_dcdA;
assign o_preB = w_dcdB;
 
generate
if (EARLY_BRANCHING!=0)
begin
reg r_early_branch, r_ljmp;
reg [(AW-1):0] r_branch_pc;
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;
 
initial r_ljmp = 1'b0;
always @(posedge i_clk)
if (i_rst)
if (i_reset)
r_ljmp <= 1'b0;
else if (i_ce)
begin
if ((r_ljmp)&&(pf_valid))
r_ljmp <= 1'b0;
`ifdef OPT_CIS
else if ((i_ce)&&(o_phase))
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]))
r_ljmp <= w_cis_ljmp;
`endif
else if ((i_ce)&&(i_pf_valid))
r_ljmp <= (w_ljmp);
end
assign o_ljmp = r_ljmp;
 
initial r_early_branch = 1'b0;
initial r_early_branch_stb = 1'b0;
always @(posedge i_clk)
if (i_rst)
r_early_branch <= 1'b0;
else if ((i_ce)&&(i_pf_valid))
if (i_reset)
begin
r_early_branch <= 1'b0;
r_early_branch_stb <= 1'b0;
end else if ((i_ce)&&(pf_valid))
begin
if (r_ljmp)
// LOD (PC),PC
r_early_branch <= 1'b1;
else if ((!iword[31])&&(iword[30:27]==`CPU_PC_REG)
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)
&&(w_cond[3]))
begin
if ((w_op[4:0]==5'h02)&&(!iword[18]))
if ((w_add)&&(!iword[`IMMSEL]))
begin
// Add x,PC
r_early_branch <= 1'b1;
else
r_early_branch_stb <= 1'b1;
end else begin
r_early_branch <= 1'b0;
end else
r_early_branch_stb <= 1'b0;
end
// LDI #x,PC is no longer supported
end else begin
r_early_branch <= 1'b0;
r_early_branch_stb <= 1'b0;
end
end else if (i_ce)
r_early_branch <= 1'b0;
begin
r_early_branch <= 1'b0;
r_early_branch_stb <= 1'b0;
end else
r_early_branch_stb <= 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];
else // Add x,PC
r_branch_pc <= i_pc
+ {{(AW-15){iword[17]}},iword[16:2]}
+ {{(AW-1){1'b0}},1'b1};
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;
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_branch_pc = {(AW){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_ljmp = 1'b0;
 
// verilator lint_off UNUSED
wire early_branch_unused;
assign early_branch_unused = w_add;
// verilator lint_on UNUSED
end endgenerate
 
 
561,14 → 635,64
// 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;
`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))
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)
// Both must be memory operations
&&(w_mem)&&(o_M)
&&(w_mem)
// Both must be writes, or both stores
&&(o_op[0] == w_cis_op[0])
// Both must be register ops
575,34 → 699,1161
&&(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
&&((o_op[0])
||(w_dcdB[3:0] != o_dcdA[3:0]))
// Needs to be to the mode, supervisor or user
&&(i_gie == o_gie)
// 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
// Same condition, or no condition before
&&((i_instruction[21:19]==o_cond[2:0])
&&((w_cond[2:0]==o_cond[2:0])
||(o_cond[2:0] == 3'h0))
// 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
// 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;
 
always @(posedge i_clk)
if (i_rst)
// 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)
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/iscachable.v
0,0 → 1,60
////////////////////////////////////////////////////////////////////////////////
//
// Filename: iscachable.v
//
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core
//
// Purpose: A helper function to both dcache and its formal properties,
// used to determine when a particular address is cachable. This
// module must be built of entirely combinatorial logic and nothing more.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2018-2019, 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
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
module iscachable(i_addr, o_cachable);
parameter ADDRESS_WIDTH=30;
localparam AW = ADDRESS_WIDTH; // Just for ease of notation below
parameter [AW-1:0] SDRAM_ADDR = 0, SDRAM_MASK = 0;
parameter [AW-1:0] BKRAM_ADDR = 30'h4000000,
BKRAM_MASK = 30'h4000000;
parameter [AW-1:0] FLASH_ADDR = 0, FLASH_MASK = 0;
 
input wire [AW-1:0] i_addr;
output reg o_cachable;
 
 
always @(*)
begin
o_cachable = 1'b0;
if ((SDRAM_ADDR !=0)&&((i_addr & SDRAM_MASK)== SDRAM_ADDR))
o_cachable = 1'b1;
else if ((FLASH_ADDR !=0)&&((i_addr & FLASH_MASK)== FLASH_ADDR))
o_cachable = 1'b1;
else if ((BKRAM_ADDR !=0)&&((i_addr & BKRAM_MASK)== BKRAM_ADDR))
o_cachable = 1'b1;
end
 
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, Gisselquist Technology, LLC
// Copyright (C) 2015,2017-2019, 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,22 → 43,32
////////////////////////////////////////////////////////////////////////////////
//
//
module memops(i_clk, i_rst, i_stb, i_lock,
`default_nettype none
//
module memops(i_clk, i_reset, 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);
parameter ADDRESS_WIDTH=30, IMPLEMENT_LOCK=0, WITH_LOCAL_BUS=0;
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;
localparam AW=ADDRESS_WIDTH;
input i_clk, i_rst;
input i_stb, i_lock;
input wire i_clk, i_reset;
input wire i_stb, i_lock;
// CPU interface
input [2:0] i_op;
input [31:0] i_addr;
input [31:0] i_data;
input [4:0] i_oreg;
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;
75,51 → 85,91
output reg [31:0] o_wb_data;
output reg [3:0] o_wb_sel;
// Wishbone inputs
input i_wb_ack, i_wb_stall, i_wb_err;
input [31:0] i_wb_data;
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
 
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);
assign gbl_stb = (i_stb)&&((WITH_LOCAL_BUS==0)||(i_addr[31:24]!=8'hff));
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);
 
initial r_wb_cyc_gbl = 1'b0;
initial r_wb_cyc_lcl = 1'b0;
always @(posedge i_clk)
if (i_rst)
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))
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 (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
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;
always @(posedge i_clk)
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
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);
 
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)
if (i_stb)
begin
o_wb_we <= i_op[0];
if (OPT_ZERO_ON_IDLE)
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 };
126,58 → 176,62
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] };
`else
default: o_wb_data <= i_data;
endcase
end else
casez({ i_op[2:1], i_addr[1:0] })
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];
`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
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
 
initial o_valid = 1'b0;
always @(posedge i_clk)
o_valid <= (!i_rst)&&((o_wb_cyc_gbl)||(o_wb_cyc_lcl))&&(i_wb_ack)&&(~o_wb_we);
if (i_reset)
o_valid <= 1'b0;
else
o_valid <= (((o_wb_cyc_gbl)||(o_wb_cyc_lcl))
&&(i_wb_ack)&&(!o_wb_we));
initial o_err = 1'b0;
always @(posedge i_clk)
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);
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;
 
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)
`ifdef ZERO_ON_IDLE
if (!i_wb_ack)
o_result <= 32'h0;
else
`endif
if ((OPT_ZERO_ON_IDLE)&&(!i_wb_ack))
o_result <= 32'h0;
else begin
casez(r_op)
4'b01??: o_result <= i_wb_data;
4'b100?: o_result <= { 16'h00, i_wb_data[31:16] };
188,25 → 242,438
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 <= (i_lock)&&((r_wb_cyc_gbl)||(lock_gbl));
lock_lcl <= (i_lock)&&((r_wb_cyc_lcl)||(lock_lcl));
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);
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/mpyop.v
0,0 → 1,319
////////////////////////////////////////////////////////////////////////////////
//
// Filename: mpyop.v
//
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core
//
// Purpose: This code has been pulled from the cpuops.v file so as to
// encapsulate the multiply component--the one component that
// (can't be) formally verified well, and so must be abstracted away.
// This separation was done to support potential future abstraction.
//
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2019, 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
// <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
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
module mpyop(i_clk,i_reset, i_stb, i_op, i_a, i_b, o_valid, o_busy, o_result, o_hi);
// The following parameter selects which multiply algorithm we use.
// Timing performance is strictly dependent upon it.
parameter IMPLEMENT_MPY = 1;
input wire i_clk, i_reset, i_stb;
input wire [1:0] i_op; // 2'b00=MPY, 2'b10=MPYUHI, 2'b11=MPYSHI
input wire [31:0] i_a, i_b;
output wire o_valid; // True if we'll be valid on the next clock;
output wire o_busy; // The multiply is busy if true
output wire [63:0] o_result; // Where we dump the multiply result
output reg o_hi; // Return the high half of the multiply
 
 
// 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.
// Given that we wish to apply this multiplexer approach to 33-bits,
// this will cost a minimum of 132 6-LUTs.
 
// i_stb instead of this_is_a_multiply_op
// o_result
// o_busy
// o_done
generate
if (IMPLEMENT_MPY == 0)
begin : MPYNONE // No multiply support.
 
assign o_result = 64'h00;
assign o_busy = 1'b0;
assign o_valid = i_stb;
always @(*) o_hi = 1'b0; // Not needed
 
`ifdef VERILATOR
// verilator lint_off UNUSED
wire [32+32+5-1:0] mpy_unused;
assign mpy_unused = { i_clk, i_reset, i_stb, i_op, i_a, i_b };
// verilator lint_on UNUSED
`endif
end else begin : IMPY
if (IMPLEMENT_MPY == 1)
begin : MPY1CK // 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 o_result = w_mpy_a_input * w_mpy_b_input;
 
assign o_busy = 1'b0;
assign o_valid = 1'b0;
always @(*) o_hi = i_op[1];
 
`ifdef VERILATOR
// verilator lint_off UNUSED
wire [3:0] mpy_unused;
assign mpy_unused = { i_clk, i_reset, i_stb, i_op[1] };
// verilator lint_on UNUSED
`endif
 
end else begin: MPN1
if (IMPLEMENT_MPY == 2)
begin : MPY2CK // 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 o_result = r_mpy_a_input * r_mpy_b_input;
assign o_busy = 1'b0;
 
reg mpypipe;
initial mpypipe = 1'b0;
always @(posedge i_clk)
if (i_reset)
mpypipe <= 1'b0;
else
mpypipe <= (i_stb);
 
assign o_valid = mpypipe; // this_is_a_multiply_op;
always @(posedge i_clk)
if (i_stb)
o_hi <= i_op[1];
 
end else begin : MPN2
if (IMPLEMENT_MPY == 3)
begin : MPY3CK // 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_reset)
mpypipe <= 2'b0;
else
mpypipe <= { mpypipe[0], i_stb };
 
// 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
 
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
 
always @(posedge i_clk)
if (i_stb)
o_hi <= i_op[1];
assign o_busy = mpypipe[0];
assign o_result = (r_sgn[1])?r_smpy_result:r_umpy_result;
assign o_valid = mpypipe[1];
 
// Results are then set on the third clock
end else begin : MPN3
if (IMPLEMENT_MPY == 4)
begin : MPY4CK // 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_reset)
mpypipe <= 3'h0;
else begin
mpypipe[0] <= i_stb;
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 (i_stb)
o_hi <= i_op[1];
end
 
assign o_busy = |mpypipe[1:0];
assign o_valid = 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 o_result = r_mpy_result;
// Fourth clock -- results are clocked into writeback
end else begin : MPYSLOW
 
// verilator lint_off UNUSED
wire unused_aux;
wire [65:0] full_result;
// verilator lint_on UNUSED
 
slowmpy #(.LGNA(6), .NA(33)) slowmpyi(i_clk, i_reset, i_stb,
{ (i_op[0])&(i_a[31]), i_a },
{ (i_op[0])&(i_b[31]), i_b }, 1'b0, o_busy,
o_valid, full_result, unused_aux);
 
assign o_result = full_result[63:0];
 
always @(posedge i_clk)
if (i_stb)
o_hi <= i_op[1];
 
end end end end end
endgenerate // All possible multiply results have been determined
 
endmodule
/core/pfcache.v
5,15 → 5,37
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core
//
// Purpose: Keeping our CPU fed with instructions, at one per clock and
// with no stalls. An unusual feature of this cache is the
// requirement that the entire cache may be cleared (if necessary).
// with only a minimum number stalls. The entire cache may also
// 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-2017, Gisselquist Technology, LLC
// Copyright (C) 2015-2019, 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
37,36 → 59,62
////////////////////////////////////////////////////////////////////////////////
//
//
module pfcache(i_clk, i_rst, i_new_pc, i_clear_cache,
`default_nettype none
//
module pfcache(i_clk, i_reset, i_new_pc, i_clear_cache,
// i_early_branch, i_from_addr,
i_stall_n, i_pc, o_i, o_pc, o_v,
i_stall_n, i_pc, o_insn, o_pc, o_valid,
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 LGCACHELEN = 8, ADDRESS_WIDTH=24,
LGLINES=5; // Log of the number of separate cache lines
localparam CACHELEN=(1<<LGCACHELEN); // Size of our cache memory
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
localparam CW=LGCACHELEN; // Short hand for LGCACHELEN
localparam PW=LGCACHELEN-LGLINES; // Size of a cache line
localparam LS=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;
//
output reg o_wb_cyc, o_wb_stb;
output wire o_wb_we;
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 [(AW-1):0] o_wb_addr;
output wire [(BUSW-1):0] o_wb_data;
//
input i_wb_ack, i_wb_stall, i_wb_err;
input [(BUSW-1):0] i_wb_data;
input wire i_wb_ack, i_wb_stall, i_wb_err;
input wire [(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
74,64 → 122,132
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] tags [0:((1<<(LGLINES))-1)];
reg [((1<<(LGLINES))-1):0] vmask;
reg [(AW-CW-1):0] cache_tags [0:((1<<(LGLINES))-1)];
reg [((1<<(LGLINES))-1):0] valid_mask;
 
reg [(AW-1):0] lastpc;
reg [(CW-1):0] rdaddr;
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):CW] tagvalipc, tagvallst;
wire [(AW-1):CW] tagval;
wire [(AW-1):PW] lasttag;
wire [(AW-1):LS] lasttag;
reg illegal_valid;
reg [(AW-1):PW] illegal_cache;
reg [(AW-1):LS] 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 [(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;
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 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.)
// 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.
//
// Here we keep track of which answer we want/need
isrc <= ((r_v)&&(i_stall_n))||(i_new_pc);
// 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 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]];
// 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.
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;
 
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;
// 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
initial tagvalipc = 0;
always @(posedge i_clk)
tagvalipc <= tags[i_pc[(CW-1):PW]];
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.
initial tagvallst = 0;
always @(posedge i_clk)
tagvallst <= tags[lastpc[(CW-1):PW]];
assign tagval = (tagsrc)?tagvalipc : tagvallst;
tagvallst <= cache_tags[lastpc[(CW+1):LS+2]];
 
// 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
139,170 → 255,724
// anyway.
initial lastpc = 0;
always @(posedge i_clk)
if (((r_v)&&(i_stall_n))||(i_clear_cache)||(i_new_pc))
lastpc <= i_pc;
if (w_advance)
lastpc <= i_pc;
 
assign lasttag = lastpc[(AW-1):PW];
assign lasttag = lastpc[(AW+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]]));
/////////////////////////////////////////////////
//
// 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]]));
 
reg [1:0] delay;
 
initial delay = 2'h3;
reg rvsrc;
always @(posedge i_clk)
if ((i_rst)||(i_clear_cache)||(i_new_pc)||((r_v)&&(i_stall_n)))
begin
// r_v <= r_v_from_pc;
rvsrc <= 1'b1;
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)
delay <= 2'h2;
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;
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
 
wire w_invalidate_result;
assign w_invalidate_result = (i_reset)||(i_clear_cache);
 
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;
always @(posedge i_clk)
r_v_from_pc <= w_v_from_pc;
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);
 
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.
//
/////////////////////////////////////////////////
//
//
initial needload = 1'b0;
always @(posedge i_clk)
r_v_from_last <= w_v_from_last;
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));
 
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);
//
// 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.
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;
 
reg last_ack;
//
// "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)&&(
(rdaddr[(PW-1):1]=={(PW-1){1'b1}})
&&((rdaddr[0])||(i_wb_ack)));
(wraddr[(LS-1):1]=={(LS-1){1'b1}})
&&((wraddr[0])||(i_wb_ack)));
 
reg needload;
initial needload = 1'b0;
initial bus_abort = 1'b0;
always @(posedge i_clk)
needload <= ((~r_v)&&(delay==0)
&&((tagvallst != lastpc[(AW-1):CW])
||(~vmask[lastpc[(CW-1):PW]]))
&&((~illegal_valid)
||(lastpc[(AW-1):PW] != illegal_cache)));
if (!o_wb_cyc)
bus_abort <= 1'b0;
else if ((i_clear_cache)||(i_new_pc))
bus_abort <= 1'b1;
 
reg last_addr;
initial last_addr = 1'b0;
always @(posedge i_clk)
last_addr <= (o_wb_cyc)&&(o_wb_addr[(PW-1):1] == {(PW-1){1'b1}})
&&((~i_wb_stall)|(o_wb_addr[0]));
 
//
// 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_rst)||(i_clear_cache))
begin
o_wb_cyc <= 1'b0;
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)
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;
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;
 
// else if (rdaddr[(PW-1):1] == {(PW-1){1'b1}})
// tags[lastpc[(CW-1):PW]] <= lastpc[(AW-1):CW];
end else if ((needload)&&(!i_new_pc))
begin
o_wb_cyc <= 1'b1;
o_wb_stb <= 1'b1;
end
 
end else if (needload)
begin
o_wb_cyc <= 1'b1;
o_wb_stb <= 1'b1;
end
// 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];
 
 
// 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)
tags[o_wb_addr[(CW-1):PW]] <= o_wb_addr[(AW-1):CW];
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}};
always @(posedge i_clk)
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}} };
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}} };
 
// 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.
// 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.
always @(posedge i_clk)
if (o_wb_cyc) // &&(i_wb_ack)
cache[rdaddr] <= i_wb_data;
if (o_wb_cyc)
cache[wraddr] <= i_wb_data;
 
// VMask ... is a section loaded?
// 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;
// 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;
initial svmask = 1'b0;
reg [(LGLINES-1):0] saddr;
always @(posedge i_clk)
if ((i_rst)||(i_clear_cache))
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)
begin
vmask <= 0;
svmask<= 1'b0;
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);
end
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;
 
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)];
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_rst)||(i_clear_cache))
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)
begin
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;
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])));
end
 
initial o_illegal = 1'b0;
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;
 
always @(posedge i_clk)
if ((i_rst)||(i_clear_cache)||(o_wb_cyc))
o_illegal <= 1'b0;
if ((f_past_valid)&&(!$past(i_reset)))
begin
if (isrc)
assert(lastpc == r_pc);
else
o_illegal <= (illegal_valid)
&&(illegal_cache == i_pc[(AW-1):PW]);
assert(lastpc + 4== r_pc);
end
 
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, Gisselquist Technology, LLC
// Copyright (C) 2015,2017,2019 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,6 → 54,8
////////////////////////////////////////////////////////////////////////////////
//
//
`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,
62,9 → 64,9
parameter RESET_ADDRESS=32'h0010_0000,
LGCACHELEN = 6, ADDRESS_WIDTH=24,
CACHELEN=(1<<LGCACHELEN), BUSW=32, AW=ADDRESS_WIDTH;
input i_clk, i_rst, i_new_pc,
input wire i_clk, i_rst, i_new_pc,
i_clear_cache, i_stall_n;
input [(AW-1):0] i_pc;
input wire [(AW-1):0] i_pc;
output reg [(BUSW-1):0] o_i;
output reg [(AW-1):0] o_pc;
output wire o_v;
74,11 → 76,11
output reg [(AW-1):0] o_wb_addr;
output wire [(BUSW-1):0] o_wb_data;
//
input i_wb_ack, i_wb_stall, i_wb_err;
input [(BUSW-1):0] i_wb_data;
input wire i_wb_ack, i_wb_stall, i_wb_err;
input wire [(BUSW-1):0] i_wb_data;
//
// Is the (data) memory unit also requesting access to the bus?
input i_wb_request;
input wire i_wb_request;
output wire o_illegal;
 
// Fixed bus outputs: we read from the bus only, never write.
120,7 → 122,7
+(3<<(LGCACHELEN-2)))
&&(|r_nvalid[(LGCACHELEN):(LGCACHELEN-1)]);
 
initial r_cache_base = RESET_ADDRESS;
initial r_cache_base = RESET_ADDRESS[(AW+1):2];
always @(posedge i_clk)
begin
if ((i_rst)||(i_clear_cache)||((o_wb_cyc)&&(i_wb_err)))
301,5 → 303,4
 
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-2017, Gisselquist Technology, LLC
// Copyright (C) 2015-2019, 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,22 → 41,35
////////////////////////////////////////////////////////////////////////////////
//
//
module pipemem(i_clk, i_rst, i_pipe_stb, i_lock,
`default_nettype none
//
module pipemem(i_clk, i_reset, 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);
parameter ADDRESS_WIDTH=30, IMPLEMENT_LOCK=0;
localparam AW=ADDRESS_WIDTH;
input i_clk, i_rst;
input i_pipe_stb, i_lock;
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;
// CPU interface
input [2:0] i_op;
input [31:0] i_addr;
input [31:0] i_data;
input [4:0] i_oreg;
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 wire o_pipe_stalled;
73,134 → 86,201
output reg [31:0] o_wb_data;
output reg [3:0] o_wb_sel;
// Wishbone inputs
input i_wb_ack, i_wb_stall, i_wb_err;
input [31:0] i_wb_data;
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
 
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];
 
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;
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, i_op[2:1], i_addr[1:0] };
fifo_oreg[wraddr] <= { i_oreg[3:0], i_op[2:1], i_addr[1:0] };
 
always @(posedge i_clk)
if ((i_rst)||(i_wb_err))
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)))
wraddr <= 0;
else if (i_pipe_stb)
wraddr <= wraddr + 1'b1;
else if (i_pipe_stb)
wraddr <= wraddr + 1'b1;
 
initial rdaddr = 0;
always @(posedge i_clk)
if ((i_rst)||(i_wb_err))
rdaddr <= 0;
else if ((i_wb_ack)&&(cyc))
rdaddr <= rdaddr + 1'b1;
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);
 
assign nxt_rdaddr = rdaddr + 1'b1;
 
wire gbl_stb, lcl_stb;
assign lcl_stb = (i_addr[31:24]==8'hff);
assign gbl_stb = (~lcl_stb);
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);
//= ((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_rst)
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))
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
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
 
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
always @(posedge i_clk)
if ((!cyc)||(!i_wb_stall))
begin
if ((!cyc)||(!i_wb_stall))
begin
if ((OPT_ZERO_ON_IDLE)&&(!i_pipe_stb))
o_wb_addr <= 0;
else
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
 
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_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
 
end
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
 
always @(posedge i_clk)
if ((i_pipe_stb)&&(~cyc))
o_wb_we <= i_op[0];
if ((i_pipe_stb)&&(!cyc))
o_wb_we <= i_op[0];
else if ((OPT_ZERO_ON_IDLE)&&(!cyc))
o_wb_we <= 1'b0;
 
initial o_valid = 1'b0;
always @(posedge i_clk)
o_valid <= (cyc)&&(i_wb_ack)&&(~o_wb_we);
if (i_reset)
o_valid <= 1'b0;
else
o_valid <= (cyc)&&(i_wb_ack)&&(!o_wb_we);
 
initial o_err = 1'b0;
always @(posedge i_clk)
o_err <= (cyc)&&(i_wb_err);
if (i_reset)
o_err <= 1'b0;
else
o_err <= ((cyc)&&(i_wb_err))||((i_pipe_stb)&&(misaligned));
assign o_busy = cyc;
 
wire [8:0] w_wreg;
wire [7:0] w_wreg;
assign w_wreg = fifo_oreg[rdaddr];
always @(posedge i_clk)
o_wreg <= w_wreg[8:4];
o_wreg <= { fifo_gie, w_wreg[7: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)
&&((i_wb_stall)||((~o_wb_stb_lcl)&&(~o_wb_stb_gbl)));
assign o_pipe_stalled = ((cyc)&&(fifo_full))||((cyc)
&&((i_wb_stall)||((!o_wb_stb_lcl)&&(!o_wb_stb_gbl))));
 
generate
if (IMPLEMENT_LOCK != 0)
210,7 → 290,12
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
217,10 → 302,319
 
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/prefetch.v
6,25 → 6,31
//
// 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, Gisselquist Technology, LLC
// Copyright (C) 2015,2017-2019, 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
48,33 → 54,40
////////////////////////////////////////////////////////////////////////////////
//
//
// 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,
`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,
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=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;
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
// 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 [31:0] o_wb_data;
output wire [(DW-1):0] o_wb_data;
// And return inputs
input i_wb_ack, i_wb_stall, i_wb_err;
input [31:0] i_wb_data;
output reg o_illegal;
input wire i_wb_ack, i_wb_stall, i_wb_err;
input wire [(DW-1):0] i_wb_data;
 
// 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;
 
83,56 → 96,497
// 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_rst)||(i_wb_ack)||(i_wb_err))
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)
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
 
reg invalid;
//
// 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.
//
initial invalid = 1'b0;
always @(posedge i_clk)
if (!o_wb_cyc)
invalid <= 1'b0;
else if ((i_new_pc)||(i_clear_cache))
invalid <= (!o_wb_stb);
if ((i_reset)||(!o_wb_cyc))
invalid <= 1'b0;
else if (i_new_pc)
invalid <= 1'b1;
 
// 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;
else if ((!o_wb_cyc)&&(i_stalled_n)&&(!invalid))
o_wb_addr <= o_wb_addr + 1'b1;
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;
 
// The instruction returned is given by the data returned from the bus.
always @(posedge i_clk)
if ((o_wb_cyc)&&(i_wb_ack))
o_i <= i_wb_data;
if ((o_wb_cyc)&&(i_wb_ack))
o_insn <= 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_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
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
 
assign o_pc = o_wb_addr;
// 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
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/slowmpy.v
0,0 → 1,217
////////////////////////////////////////////////////////////////////////////////
//
// Filename: slowmpy.v
//
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core
//
// Purpose: This is a signed (OPT_SIGNED=1) or unsigned (OPT_SIGNED=0)
// multiply designed for low logic and slow data signals. It
// takes one clock per bit plus two more to complete the multiply.
//
// The OPT_SIGNED version of this algorithm was found on Wikipedia at
// https://en.wikipedia.org/wiki/Binary_multiplier.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2018-2019, 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
// <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
//
//
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
//
module slowmpy(i_clk, i_reset, i_stb, i_a, i_b, i_aux, o_busy,
o_done, o_p, o_aux);
parameter LGNA = 6;
parameter [LGNA:0] NA = 33;
parameter [0:0] OPT_SIGNED = 1'b1;
localparam NB = NA; // Must be = NA for OPT_SIGNED to work
//
input wire i_clk, i_reset;
//
input wire i_stb;
input wire signed [(NA-1):0] i_a;
input wire signed [(NB-1):0] i_b;
input wire i_aux;
output reg o_busy, o_done;
output reg signed [(NA+NB-1):0] o_p;
output reg o_aux;
 
reg [LGNA-1:0] count;
reg [NA-1:0] p_a;
reg [NB-1:0] p_b;
reg [NA+NB-1:0] partial;
reg aux;
 
reg almost_done;
 
wire pre_done;
assign pre_done = (count == 0);
initial almost_done = 1'b0;
always @(posedge i_clk)
almost_done <= (!i_reset)&&(o_busy)&&(pre_done);
 
initial aux = 0;
initial o_done = 0;
initial o_busy = 0;
always @(posedge i_clk)
if (i_reset)
begin
aux <= 0;
o_done <= 0;
o_busy <= 0;
end else if ((!o_busy)&&(i_stb))
begin
o_done <= 0;
o_busy <= 1;
aux <= i_aux;
end else if ((o_busy)&&(almost_done))
begin
o_done <= 1;
o_busy <= 0;
end else
o_done <= 0;
 
wire [NA-1:0] pwire;
assign pwire = (p_b[0] ? p_a : 0);
 
always @(posedge i_clk)
if (!o_busy)
begin
count <= NA[LGNA-1:0]-1;
partial <= 0;
p_a <= i_a;
p_b <= i_b;
end else begin
p_b <= (p_b >> 1);
// partial[NA+NB-1:NB] <= partial[NA+NB
partial[NB-2:0] <= partial[NB-1:1];
if ((OPT_SIGNED)&&(pre_done))
partial[NA+NB-1:NB-1] <= { 1'b0, partial[NA+NB-1:NB]} +
{ 1'b0, pwire[NA-1], ~pwire[NA-2:0] };
else if (OPT_SIGNED)
partial[NA+NB-1:NB-1] <= {1'b0,partial[NA+NB-1:NB]} +
{ 1'b0, !pwire[NA-1], pwire[NA-2:0] };
else
partial[NA+NB-1:NB-1] <= {1'b0, partial[NA+NB-1:NB]}
+ ((p_b[0]) ? {1'b0,p_a} : 0);
count <= count - 1;
end
 
always @(posedge i_clk)
if (almost_done)
begin
if (OPT_SIGNED)
o_p <= partial[NA+NB-1:0]
+ {1'b1,{(NA-2){1'b0}},1'b1, {(NB){1'b0}}};
else
o_p <= partial[NA+NB-1:0];
o_aux <= aux;
end
 
`ifdef FORMAL
`define ASSERT assert
`ifdef SLOWMPY
`define ASSUME assume
`else
`define ASSUME assert
`endif
 
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(almost_done == 0);
`ASSERT(o_done == 0);
`ASSERT(o_busy == 0);
`ASSERT(aux == 0);
end
 
// Assumptions about our inputs
always @(posedge i_clk)
if ((f_past_valid)&&(!$past(i_reset))&&($past(i_stb))&&($past(o_busy)))
begin
`ASSUME(i_stb);
`ASSUME($stable(i_a));
`ASSUME($stable(i_b));
end
 
//
// For now, just formally verify our internal signaling
//
 
always @(posedge i_clk)
`ASSERT(almost_done == (o_busy&&(&count)));
 
always @(*)
if (!(&count[LGNA-1:1])||(count[0]))
`ASSERT(!o_done);
 
always @(posedge i_clk)
if (o_done)
`ASSERT(!o_busy);
always @(posedge i_clk)
if (!o_busy)
`ASSERT(!almost_done);
 
reg [NA-1:0] f_a, f_b;
always @(posedge i_clk)
if ((i_stb)&&(!o_busy))
begin
f_a <= i_a;
f_b <= i_b;
end
 
always @(*)
if (o_done)
begin
if ((f_a == 0)||(f_b == 0))
`ASSERT(o_p == 0);
else
`ASSERT(o_p[NA+NB-1] == f_a[NA-1] ^ f_b[NA-1]);
end
 
always @(posedge i_clk)
cover(o_done);
 
reg f_past_done;
initial f_past_done = 1'b0;
always @(posedge i_clk)
if (o_done)
f_past_done = 1'b1;
 
always @(posedge i_clk)
cover((o_done)&&(f_past_done));
`endif
endmodule
/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_rst)||(clear_pipeline))
// if ((i_reset)||(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-2017, Gisselquist Technology, LLC
//
// Copyright (C) 2015-2019, 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,7 → 98,13
////////////////////////////////////////////////////////////////////////////////
//
//
`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
117,7 → 123,7
`include "cpudefs.v"
//
//
module zipcpu(i_clk, i_rst, i_interrupt,
module zipcpu(i_clk, i_reset, 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,
131,12 → 137,14
// Accounting/CPU usage interface
o_op_stall, o_pf_stall, o_i_count
`ifdef DEBUG_SCOPE
, o_debug
, o_debug // , o_dcache_debug
`endif
);
// Parameters
//{{{
parameter [31:0] RESET_ADDRESS=32'h0100000;
parameter ADDRESS_WIDTH=30,
LGICACHE=8;
LGICACHE=12;
`ifdef OPT_MULTIPLY
parameter IMPLEMENT_MPY = `OPT_MULTIPLY;
`else
143,30 → 151,64
parameter IMPLEMENT_MPY = 0;
`endif
`ifdef OPT_DIVIDE
parameter IMPLEMENT_DIVIDE = 1;
parameter [0:0] IMPLEMENT_DIVIDE = 1;
`else
parameter IMPLEMENT_DIVIDE = 0;
parameter [0:0] IMPLEMENT_DIVIDE = 0;
`endif
`ifdef OPT_IMPLEMENT_FPU
parameter IMPLEMENT_FPU = 1,
parameter [0:0] IMPLEMENT_FPU = 1;
`else
parameter IMPLEMENT_FPU = 0,
parameter [0:0] IMPLEMENT_FPU = 0;
`endif
IMPLEMENT_LOCK=1;
`ifdef OPT_EARLY_BRANCHING
parameter EARLY_BRANCHING = 1;
parameter [0:0] EARLY_BRANCHING = 1;
`else
parameter EARLY_BRANCHING = 0;
parameter [0:0] EARLY_BRANCHING = 0;
`endif
parameter WITH_LOCAL_BUS = 1;
`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;
localparam AW=ADDRESS_WIDTH;
localparam [(AW-1):0] RESET_BUS_ADDRESS = RESET_ADDRESS[(AW+1):2];
input i_clk, i_rst, i_interrupt;
parameter F_LGDEPTH=8;
 
//}}}
// I/O declarations
//{{{
input wire i_clk, i_reset, i_interrupt;
// Debug interface -- inputs
input i_halt, i_clear_pf_cache;
input [4:0] i_dbg_reg;
input i_dbg_we;
input [31:0] i_dbg_data;
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;
// Debug interface -- outputs
output wire o_dbg_stall;
output reg [31:0] o_dbg_reg;
179,9 → 221,9
output wire [31:0] o_wb_data;
output wire [3:0] o_wb_sel;
// Wishbone interface -- inputs
input i_wb_ack, i_wb_stall;
input [31:0] i_wb_data;
input i_wb_err;
input wire i_wb_ack, i_wb_stall;
input wire [31:0] i_wb_data;
input wire i_wb_err;
// Accounting outputs ... to help us count stalls and usage
output wire o_op_stall;
output wire o_pf_stall;
189,7 → 231,9
//
`ifdef DEBUG_SCOPE
output reg [31:0] o_debug;
// output wire [31:0] o_dcache_debug;
`endif
//}}}
 
 
// Registers
202,11 → 246,7
// that logic.
//
(* ram_style = "distributed" *)
`ifdef OPT_NO_USERMODE
reg [31:0] regset [0:15];
`else
reg [31:0] regset [0:31];
`endif
reg [31:0] regset [0:(OPT_NO_USERMODE)? 15:31];
 
// Condition codes
// (BUS, TRAP,ILL,BREAKEN,STEP,GIE,SLEEP ), V, N, C, Z
213,7 → 253,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;
wire break_pending, trap, gie, ubreak, pending_interrupt;
wire w_clear_icache, ill_err_u;
reg ill_err_i;
reg ibus_err_flag;
223,7 → 263,7
wire ihalt_phase, uhalt_phase;
 
// The master chip enable
wire master_ce;
wire master_ce, master_stall;
 
//
//
230,19 → 270,30
// 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;
 
wire dcd_stalled;
wire pf_cyc, pf_stb, pf_we, pf_busy, pf_ack, pf_stall, pf_err;
reg dcd_stalled;
wire pf_cyc, pf_stb, pf_we, pf_ack, pf_stall, pf_err;
wire [(AW-1):0] pf_addr;
wire [31:0] pf_data;
wire [31:0] pf_instruction;
wire [(AW-1):0] pf_instruction_pc;
wire pf_valid, pf_gie, pf_illegal;
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
 
assign clear_pipeline = new_pc;
//}}}
 
//
//
// PIPELINE STAGE #2 :: Instruction Decode
249,12 → 300,13
// 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;
wire [4:0] dcd_A, dcd_B, dcd_R, dcd_preA, dcd_preB;
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,
262,17 → 314,21
dcd_wF, dcd_gie, dcd_break, dcd_lock,
dcd_pipe, dcd_ljmp;
wire dcd_valid;
wire [AW:0] dcd_pc /* verilator public_flat */;
wire [AW+1: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;
wire [(AW-1):0] dcd_branch_pc;
wire dcd_early_branch, dcd_early_branch_stb;
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;
//}}}
 
 
//
282,20 → 338,29
//
//
//
//{{{
// Now, let's read our operands
reg [4:0] alu_reg;
wire [3:0] op_opn;
wire [4:0] op_R;
reg [4:0] op_R;
reg op_Rcc;
reg [4:0] op_Aid, op_Bid;
reg op_rA, op_rB;
reg [31:0] r_op_Av, r_op_Bv;
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 [(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 op_wR, op_wF;
wire op_gie, op_Rcc;
wire [14:0] op_Fl;
wire op_gie;
wire [3:0] op_Fl;
reg [6:0] r_op_F;
wire [7:0] op_F;
wire op_ce, op_phase, op_pipe, op_change_data_ce;
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;
// Some pipeline control wires
reg op_illegal;
wire op_break;
304,7 → 369,10
`ifdef VERILATOR
reg op_sim /* verilator public_flat */;
reg [22:0] op_sim_immv /* verilator public_flat */;
`else
wire op_sim = 1'b0;
`endif
//}}}
 
 
//
313,7 → 381,8
// 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;
326,11 → 395,10
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;
338,43 → 406,62
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;
 
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 fpu_ce, fpu_error, fpu_busy, fpu_valid;
wire [31:0] fpu_result;
wire [3:0] fpu_flags;
reg adf_ce_unconditional;
 
assign fpu_ce = (master_ce)&&(!clear_pipeline)&&(op_valid_fpu)
&&(!mem_rdbusy)&&(!div_busy)&&(!fpu_busy)
&&(set_cond);
wire bus_lock;
 
wire adf_ce_unconditional;
reg dbgv, dbg_clear_pipe;
reg [31:0] dbg_val;
 
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))&&(!o_break)&&(!sleep);
assign master_ce = ((!i_halt)||(alu_phase))
&&(!cc_write_hold)&&(!o_break)&&(!sleep);
 
 
//
388,45 → 475,50
// PIPELINE STAGE #2 :: Instruction Decode
// Calculate stall conditions
 
`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
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);
//
// PIPELINE STAGE #3 :: Read Operands
// Calculate stall conditions
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);
//{{{
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)));
 
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)
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)
)
||(dcd_valid)&&(
// Stall if we need to wait for an operand A
441,25 → 533,36
// 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);
 
`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
end else begin // !OPT_PIPELINED
 
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
469,18 → 572,31
// 4. Last case: Stall if we would otherwise move a break instruction
// through the ALU. Break instructions are not allowed through
// the ALU.
`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
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
//
 
//
490,38 → 606,55
assign mem_ce = (master_ce)&&(op_valid_mem)&&(!mem_stalled)
&&(!clear_pipeline);
 
`ifdef OPT_PIPELINED_BUS_ACCESS
assign mem_stalled = (!master_ce)||(alu_busy)||((op_valid_mem)&&(
generate if (OPT_PIPELINED_BUS_ACCESS)
begin
 
assign mem_stalled = (master_stall)||((op_valid_mem)&&(
(mem_pipe_stalled)
||(prelock_stall)
||(bus_err)||(div_error)
||((!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_id[4] == op_gie)
||((wr_reg_ce)
&&((wr_write_pc)||(wr_write_cc)))));
`else
`ifdef OPT_PIPELINED
assign mem_stalled = (mem_busy)||((op_valid_mem)&&(
(!master_ce)
end else if (OPT_PIPELINED)
begin
assign mem_stalled = (master_stall)||((op_valid_mem)&&(
(bus_err)||(div_error)||(mem_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_id[4] == op_gie)&&((wr_write_pc)||(wr_write_cc)))));
`else
assign mem_stalled = (op_valid_mem)&&(!master_ce);
`endif
`endif
||((wr_reg_ce)
&&((wr_write_pc)||(wr_write_cc)))));
end else begin
 
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
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);
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);
 
//
//
528,19 → 661,37
// PIPELINE STAGE #1 :: Prefetch
//
//
wire pf_stalled;
//{{{
assign pf_stalled = (dcd_stalled)||(dcd_phase);
 
wire pf_new_pc;
assign pf_new_pc = (new_pc)||((dcd_early_branch)&&(!clear_pipeline));
assign pf_new_pc = (new_pc)||((dcd_early_branch_stb)&&(!clear_pipeline));
 
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_request_address = ((dcd_early_branch_stb)&&(!clear_pipeline))
? dcd_branch_pc:pf_pc;
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_rst), pf_new_pc, w_clear_icache,
//{{{
pf(i_clk, (i_reset), pf_new_pc, w_clear_icache,
(!pf_stalled),
pf_request_address,
pf_instruction, pf_instruction_pc,
547,14 → 698,13
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_rst, pf_new_pc,
w_clear_icache,
//{{{
pf(i_clk, i_reset, pf_new_pc, w_clear_icache,
(!pf_stalled),
pf_request_address,
pf_instruction, pf_instruction_pc,
562,12 → 712,14
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_rst, pf_new_pc, w_clear_icache,
//{{{
pf(i_clk, i_reset, pf_new_pc, w_clear_icache,
// dcd_pc,
(!pf_stalled),
pf_request_address,
575,187 → 727,275
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, LGICACHE, ADDRESS_WIDTH)
pf(i_clk, i_rst, pf_new_pc,
pipefetch #({RESET_BUS_ADDRESS, 2'b00}, LGICACHE, ADDRESS_WIDTH)
//{{{
pf(i_clk, i_reset, pf_new_pc,
w_clear_icache, (!pf_stalled),
(new_pc)?pf_pc[(AW+1):2]:dcd_branch_pc,
(new_pc)?pf_pc: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
//}}}
 
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),
//
//
// 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),
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_gie,
dcd_illegal, dcd_pc,
{ 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, dcd_early_branch_stb,
dcd_branch_pc, dcd_ljmp,
dcd_pipe,
dcd_sim, dcd_sim_immv);
dcd_sim, dcd_sim_immv
`ifdef FORMAL
, f_dcd_insn_word, f_dcd_insn_gie
`endif
);
assign dcd_gie = pf_gie;
//}}}
 
`ifdef OPT_PIPELINED_BUS_ACCESS
reg r_op_pipe;
//
//
// 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)
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;
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;
 
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;
`else
assign op_pipe = 1'b0;
`endif
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
 
//
//
// PIPELINE STAGE #3 :: Read Operands (Registers)
//
//
`ifdef OPT_NO_USERMODE
assign w_op_Av = regset[dcd_A[3:0]];
assign w_op_Bv = regset[dcd_B[3:0]];
`else
assign w_op_Av = regset[dcd_A];
assign w_op_Bv = regset[dcd_B];
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
`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,
`ifdef OPT_PIPELINED
1'b1,
`else
1'b0,
`endif
OPT_PIPELINED,
`ifdef OPT_TRADITIONAL_CACHE
1'b1,
`else
1'b0,
`endif
`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
(EARLY_BRANCHING > 0)? 1'b1:1'b0,
OPT_PIPELINED_BUS_ACCESS,
OPT_CIS
};
//}}}
 
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 } };
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 };
 
generate
if (AW < 30)
assign w_pcA_v[31:(AW+2)] = 0;
always @(*)
w_pcA_v[31:(AW+2)] = 0;
endgenerate
 
`ifdef OPT_PIPELINED
reg [4:0] op_Aid, op_Bid;
reg op_rA, op_rB;
always @(posedge i_clk)
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)
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_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);
end
`endif
 
always @(posedge i_clk)
if (op_ce)
end else begin
 
always @(*)
begin
`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
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);
end
 
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 } };
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 };
generate
if (AW < 30)
assign w_pcB_v[31:(AW+2)] = 0;
always @(*)
w_pcB_v[31:(AW+2)] = 0;
endgenerate
 
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 @(*)
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;
 
always @(posedge i_clk)
`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 ((!OPT_PIPELINED)||(op_ce))
begin
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;
`endif
end else if ((OPT_PIPELINED)&&(op_rB)
&&(wr_reg_ce)&&(op_Bid == wr_reg_id))
r_op_Bv <= wr_gpreg_vl;
 
// 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
767,25 → 1007,27
// below, arriving at what we finally want in the (now wire net)
// op_F.
always @(posedge i_clk)
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 }
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 }
assign op_F = { r_op_F[3], r_op_F[6:0] };
 
wire w_op_valid;
assign w_op_valid = (!clear_pipeline)&&(dcd_valid)&&(!dcd_ljmp)&&(!dcd_early_branch);
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;
792,29 → 1034,31
initial op_valid_div = 1'b0;
initial op_valid_fpu = 1'b0;
always @(posedge i_clk)
if (clear_pipeline)
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)
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.
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);
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);
end else if ((adf_ce_unconditional)||(mem_ce))
begin
op_valid <= 1'b0;
823,6 → 1067,14
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
833,74 → 1085,76
// 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 ((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;
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);
assign op_break = r_op_break;
 
`ifdef OPT_PIPELINED
generate
if (IMPLEMENT_LOCK != 0)
generate if ((!OPT_PIPELINED)||(!OPT_LOCK))
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)&&(!clear_pipeline);
if (clear_pipeline)
r_op_lock <= 1'b0;
else if (op_ce)
r_op_lock <= (dcd_valid)&&(dcd_lock)
&&(!dcd_illegal);
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 (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 ((i_reset)||(clear_pipeline))
op_illegal <= 1'b0;
else if (OPT_PIPELINED)
begin
if (op_ce)
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)
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);
if (dcd_valid)
op_illegal <= (!dcd_ljmp)&&(!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 @(*)
910,7 → 1164,7
end
`else
always @(posedge i_clk)
if (op_change_data_ce)
if (op_ce)
begin
op_sim <= dcd_sim;
op_sim_immv <= dcd_sim_immv;
918,48 → 1172,68
`endif
`endif
 
reg [3:0] r_op_opn;
reg [4:0] r_op_R;
reg r_op_Rcc;
reg r_op_gie;
 
initial r_op_gie = 1'b0;
always @(posedge i_clk)
if (op_change_data_ce)
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)
begin
// Which ALU operation? Early branches are
// unimplemented moves
r_op_opn <= (dcd_early_branch) ? 4'hf : dcd_opn;
r_op_opn <= ((dcd_early_branch)||(dcd_illegal))
? `CPU_MOV_OP : 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;
end
 
//
op_pc <= (dcd_early_branch)?dcd_branch_pc:dcd_pc[AW:1];
end
end endgenerate
 
assign op_opn = r_op_opn;
assign op_R = r_op_R;
assign op_gie = r_op_gie;
assign op_Rcc = r_op_Rcc;
assign op_gie = gie;
 
assign op_Fl = (op_gie)?(w_uflags):(w_iflags);
assign op_Fl = (op_gie)?(w_uflags[3:0]):(w_iflags[3:0]);
 
`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
generate if (OPT_CIS)
begin : OPT_CIS_OP_PHASE
 
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
972,14 → 1246,18
// 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.
`ifdef OPT_PIPELINED
assign op_Av = ((wr_reg_ce)&&(wr_reg_id == op_Aid)) // &&(op_rA))
generate if (OPT_PIPELINED)
begin
 
assign op_Av = ((wr_reg_ce)&&(wr_reg_id == op_Aid))
? wr_gpreg_vl : r_op_Av;
`else
assign op_Av = r_op_Av;
`endif
 
`ifdef OPT_PIPELINED
end else begin
 
assign op_Av = r_op_Av;
 
end endgenerate
 
// 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 ...)
986,24 → 1264,27
// The operation might set flags, and we wish to read the
// CC register
// OR ... (No other conditions)
assign dcd_A_stall = (dcd_rA) // &&(dcd_valid) is checked for elsewhere
generate if (OPT_PIPELINED)
begin
 
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));
`else
// There are no pipeline hazards, if we aren't pipelined
assign dcd_A_stall = 1'b0;
`endif
end else begin
 
`ifdef OPT_PIPELINED
assign op_Bv = ((wr_reg_ce)&&(wr_reg_id == op_Bid)&&(op_rB))
// 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))
? wr_gpreg_vl: r_op_Bv;
`else
assign op_Bv = r_op_Bv;
`endif
 
`ifdef OPT_PIPELINED
generate if (OPT_PIPELINED)
begin
// 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 ...)
1012,6 → 1293,7
// 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
1039,6 → 1321,8
((!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
1049,45 → 1333,75
// ||((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_Acc))
||((dcd_rB)&&(dcd_Bcc)))
&&(op_valid)&&(op_Rcc);
//}}}
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));
// &&(dcd_valid) is checked for elsewhere
`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
//}}}
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
 
//}}}
//
//
// PIPELINE STAGE #4 :: Apply Instruction
//
//
cpuops #(IMPLEMENT_MPY) doalu(i_clk, (clear_pipeline),
// ALU
cpuops #(IMPLEMENT_MPY) doalu(i_clk, ((i_reset)||(clear_pipeline)),
//{{{
alu_ce, op_opn, op_Av, op_Bv,
alu_result, alu_flags, alu_valid, alu_busy);
//}}}
 
generate
if (IMPLEMENT_DIVIDE != 0)
begin
div thedivide(i_clk, (clear_pipeline), div_ce, op_opn[0],
// 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],
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
//}}}
 
generate
if (IMPLEMENT_FPU != 0)
begin
// (Non-existent) FPU
//{{{
generate if (IMPLEMENT_FPU != 0)
begin : FPU
//
// sfpu thefpu(i_clk, i_rst, fpu_ce,
// sfpu thefpu(i_clk, i_reset, fpu_ce, op_opn[2:0],
// op_Av, op_Bv, fpu_busy, fpu_valid, fpu_err, fpu_result,
// fpu_flags);
//
1103,13 → 1417,16
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;
always @(posedge i_clk)
if (i_rst)
generate if (OPT_PIPELINED)
begin
always @(posedge i_clk)
if (i_reset)
begin
alu_wR <= 1'b0;
alu_wF <= 1'b0;
1116,90 → 1433,130
end else if (alu_ce)
begin
// alu_reg <= op_R;
alu_wR <= (op_wR)&&(set_cond);
alu_wF <= (op_wF)&&(set_cond);
alu_wR <= (op_wR)&&(set_cond)&&(!op_illegal);
alu_wF <= (op_wF)&&(set_cond)&&(!op_illegal);
end else if (!alu_busy) begin
// These are strobe signals, so clear them if not
// set for any particular clock
alu_wR <= (i_halt)&&(i_dbg_we);
alu_wR <= (r_halted)&&(i_dbg_we);
alu_wF <= 1'b0;
end
end else begin
 
`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
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_PIPELINED
always @(posedge i_clk)
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)
if (adf_ce_unconditional)
alu_reg <= op_R;
else if ((i_halt)&&(i_dbg_we))
else if ((r_halted)&&(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)
dbgv <= (!i_rst)&&(i_halt)&&(i_dbg_we)&&(r_halted);
reg [31:0] dbg_val;
if (i_reset)
dbgv <= 0;
else
dbgv <= (i_dbg_we)&&(r_halted);
 
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 ((adf_ce_unconditional)||(mem_ce))
r_alu_gie <= op_gie;
assign alu_gie = r_alu_gie;
`else
assign alu_gie = op_gie;
`endif
`endif
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;
 
`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)))
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)))
r_alu_pc <= op_pc;
assign alu_pc = r_alu_pc;
`else
assign alu_pc = op_pc;
`endif
assign alu_pc = r_alu_pc;
 
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);
end else begin
 
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)
1211,16 → 1568,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_rst)
if (i_reset)
mem_pc_valid <= 1'b0;
else
mem_pc_valid <= (mem_ce);
 
wire bus_lock;
`ifdef OPT_PIPELINED
// Bus lock logic
//{{{
generate
if (IMPLEMENT_LOCK != 0)
begin
if ((OPT_PIPELINED)&&(!OPT_LOCK))
begin : BUSLOCK
reg r_prelock_stall;
 
initial r_prelock_stall = 1'b0;
1235,6 → 1592,7
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;
1260,12 → 1618,51
assign prelock_stall = 1'b0;
assign bus_lock = 1'b0;
end endgenerate
`else
assign bus_lock = 1'b0;
//}}}
 
// 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)
`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
 
`ifdef OPT_PIPELINED_BUS_ACCESS
pipemem #(AW,IMPLEMENT_LOCK) domem(i_clk, i_rst,(mem_ce)&&(set_cond), bus_lock,
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,
(op_opn[2:0]), op_Bv, op_Av, op_R,
mem_busy, mem_pipe_stalled,
mem_valid, bus_err, mem_wreg, mem_result,
1272,10 → 1669,22
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);
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
 
`else // PIPELINED_BUS_ACCESS
memops #(AW,IMPLEMENT_LOCK,WITH_LOCAL_BUS) domem(i_clk, i_rst,
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,
//{{{
(mem_ce)&&(set_cond), bus_lock,
(op_opn[2:0]), op_Bv, op_Av, op_R,
mem_busy,
1283,14 → 1692,28
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);
assign mem_pipe_stalled = 1'b0;
`endif // PIPELINED_BUS_ACCESS
assign mem_rdbusy = ((mem_busy)&&(!mem_we));
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
 
assign mem_rdbusy = (mem_busy)&&((!OPT_PIPELINED)||(!mem_we));
 
// Either the prefetch or the instruction gets the memory bus, but
// never both.
wbdblpriarb #(32,AW) pformem(i_clk, i_rst,
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,
//{{{
// 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,
1310,10 → 1733,20
// 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);
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
);
//}}}
//}}}
 
 
 
//
//
//
1324,6 → 1757,7
//
// 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
1341,20 → 1775,23
assign wr_reg_ce = (dbgv)||(mem_valid)
||((!clear_pipeline)&&(!alu_illegal)
&&(((alu_wR)&&(alu_valid))
||(div_valid)||(fpu_valid)));
||((div_valid)&&(!div_error))
||((fpu_valid)&&(!fpu_error))));
// 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.
`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
generate if (OPT_NO_USERMODE)
begin
assign wr_reg_id[3:0] = (mem_valid)
? mem_wreg[3:0] : alu_reg[3:0];
 
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});
1369,19 → 1806,30
:((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)||(div_valid)||(fpu_valid))&&(!clear_pipeline)&&(!alu_illegal);
assign wr_flags_ce = (alu_wF)&&((alu_valid)
||(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,
1426,30 → 1874,35
// condition has taken place.
initial break_en = 1'b0;
always @(posedge i_clk)
if ((i_rst)||(i_halt))
if ((i_reset)||(i_halt))
break_en <= 1'b0;
else if ((wr_reg_ce)&&(wr_write_scc))
break_en <= wr_spreg_vl[`CPU_BREAK_BIT];
 
`ifdef OPT_PIPELINED
reg r_break_pending;
generate if (OPT_PIPELINED)
begin : GEN_PENDING_BREAK
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 <= (!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
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
 
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))
1462,80 → 1915,108
// 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;
`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;
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;
 
// 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
// 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
 
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_rst)
if (i_reset)
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
`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)&&(
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)&&(
// On interrupt (obviously)
((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))
((pending_interrupt)
&&(!alu_phase)&&(!bus_lock)&&(!mem_busy))
//
// 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))
1545,60 → 2026,66
&&(((wr_reg_ce)&&(wr_spreg_vl[`CPU_GIE_BIT])
&&(wr_write_scc))
);
`endif
end endgenerate
 
`ifdef OPT_NO_USERMODE
assign gie = 1'b0;
`else
reg r_gie;
generate if (OPT_NO_USERMODE)
begin
assign gie = 1'b0;
end else begin : SET_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
reg r_gie;
 
`ifdef OPT_NO_USERMODE
assign trap = 1'b0;
assign ubreak = 1'b0;
`else
reg r_trap;
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
 
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]);
generate if (OPT_NO_USERMODE)
begin
 
reg r_ubreak;
assign trap = 1'b0;
assign ubreak = 1'b0;
 
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]);
end else begin : SET_TRAP_N_UBREAK
 
assign trap = r_trap;
assign ubreak = r_ubreak;
`endif
reg r_trap;
 
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]);
 
`ifdef OPT_ILLEGAL_INSTRUCTION
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
 
 
initial ill_err_i = 1'b0;
always @(posedge i_clk)
if (i_rst)
if (i_reset)
ill_err_i <= 1'b0;
// Only the debug interface can clear this bit
else if ((dbgv)&&(wr_write_scc))
1606,35 → 2093,37
else if ((alu_illegal)&&(!alu_gie)&&(!clear_pipeline))
ill_err_i <= 1'b1;
 
`ifdef OPT_NO_USERMODE
assign ill_err_u = 1'b0;
`else
reg r_ill_err_u;
generate if (OPT_NO_USERMODE)
begin
 
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;
assign ill_err_u = 1'b0;
 
assign ill_err_u = r_ill_err_u;
`endif
`else
assign ill_err_u = 1'b0;
assign ill_err_i = 1'b0;
`endif
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
 
// 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_rst)
if (i_reset)
ibus_err_flag <= 1'b0;
else if ((dbgv)&&(wr_write_scc))
ibus_err_flag <= (ibus_err_flag)&&(wr_spreg_vl[`CPU_BUSERR_BIT]);
1642,26 → 2131,29
ibus_err_flag <= 1'b1;
// User bus error flag -- if ever set, it will cause an interrupt to
// supervisor mode.
`ifdef OPT_NO_USERMODE
assign ubus_err_flag = 1'b0;
`else
reg r_ubus_err_flag;
generate if (OPT_NO_USERMODE)
begin
 
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;
assign ubus_err_flag = 1'b0;
 
assign ubus_err_flag = r_ubus_err_flag;
`endif
end else begin : SET_USER_BUSERR
 
generate
if (IMPLEMENT_DIVIDE != 0)
begin
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
reg r_idiv_err_flag, r_udiv_err_flag;
 
// Supervisor/interrupt divide (by zero) error flag -- this will
1669,7 → 2161,7
// to be able to tell if/why the CPU crashed.
initial r_idiv_err_flag = 1'b0;
always @(posedge i_clk)
if (i_rst)
if (i_reset)
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]);
1677,37 → 2169,39
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;
 
assign udiv_err_flag = r_udiv_err_flag;
`endif
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
end else begin
assign idiv_err_flag = 1'b0;
assign udiv_err_flag = 1'b0;
end endgenerate
 
generate
if (IMPLEMENT_FPU !=0)
begin
generate if (IMPLEMENT_FPU !=0)
begin : FPUERR
// 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_rst)
if (i_reset)
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]);
1717,7 → 2211,7
// a sudden switch interrupt to supervisor mode.
initial r_ufpu_err_flag = 1'b0;
always @(posedge i_clk)
if ((i_rst)&&(w_release_from_interrupt))
if ((i_reset)&&(w_release_from_interrupt))
r_ufpu_err_flag <= 1'b0;
else if (((!alu_gie)||(dbgv))&&(wr_reg_ce)
&&(wr_write_ucc))
1732,39 → 2226,47
assign ufpu_err_flag = 1'b0;
end endgenerate
 
`ifdef OPT_CIS
reg r_ihalt_phase;
generate if (OPT_CIS)
begin : GEN_IHALT_PHASE
reg r_ihalt_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;
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;
 
assign ihalt_phase = r_ihalt_phase;
assign ihalt_phase = r_ihalt_phase;
end else begin : GEN_IHALT_PHASE
 
`ifdef OPT_NO_USERMODE
assign uhalt_phase = 1'b0;
`else
reg r_uhalt_phase;
assign ihalt_phase = 1'b0;
 
initial r_uhalt_phase = 0;
always @(posedge i_clk)
if ((i_rst)||(w_release_from_interrupt))
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))
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_ucc))
r_uhalt_phase <= wr_spreg_vl[`CPU_PHASE_BIT];
else if ((!alu_gie)&&(wr_reg_ce)&&(wr_write_pc)
&&(wr_reg_id[4]))
r_uhalt_phase <= wr_spreg_vl[1];
 
assign uhalt_phase = r_uhalt_phase;
`endif
`else
assign ihalt_phase = 1'b0;
assign uhalt_phase = 1'b0;
`endif
assign uhalt_phase = r_uhalt_phase;
 
end endgenerate
 
//
// Write backs to the PC register, and general increments of it
// We support two: upc and ipc. If the instruction is normal,
1775,134 → 2277,217
// 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?
`ifdef OPT_NO_USERMODE
assign upc = {(AW+2){1'b0}};
`else
reg [(AW+1):0] r_upc;
generate if (OPT_NO_USERMODE)
begin
 
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 ((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
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;
 
initial pf_pc = { RESET_BUS_ADDRESS, 2'b00 };
always @(posedge i_clk)
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 };
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 };
 
initial last_write_to_cc = 1'b0;
always @(posedge i_clk)
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 (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 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_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;
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;
assign w_clear_icache = r_clear_icache;
 
initial new_pc = 1'b1;
always @(posedge i_clk)
if ((i_rst)||(w_clear_icache))
if ((i_reset)||(w_clear_icache)||(dbg_clear_pipe))
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))
// 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))
new_pc <= 1'b1;
else
new_pc <= 1'b0;
 
//
// The debug interface
// The debug write-back interface
//{{{
wire [31:0] w_debug_pc;
`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])
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])
? { upc[(AW+1):2], uhalt_phase, 1'b0 }
: { ipc[(AW+1):2], ihalt_phase, 1'b0 } };
`endif
end endgenerate
 
generate
if (AW<30)
assign w_debug_pc[31:(AW+2)] = 0;
endgenerate
 
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)
generate if (OPT_NO_USERMODE)
begin : NO_USER_SETDBG
 
always @(posedge i_clk)
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;
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
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
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)
always @(posedge i_clk)
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;
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
end
`endif
end
 
end endgenerate
 
always @(posedge i_clk)
o_dbg_cc <= { o_break, bus_err, gie, sleep };
 
`ifdef OPT_PIPELINED
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;
 
always @(posedge i_clk)
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)));
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;
`else
always @(posedge i_clk)
r_halted <= (i_halt)&&((op_valid)||(i_rst));
assign o_dbg_stall = !r_halted;
`endif
assign o_dbg_stall = !r_halted;
//}}}
 
//}}}
 
//
//
// Produce accounting outputs: Account for any CPU stalls, so we can
1914,52 → 2499,1884
assign o_i_count = (alu_pc_valid)&&(!clear_pipeline);
 
`ifdef DEBUG_SCOPE
//{{{
 
reg debug_trigger;
initial debug_trigger = 1'b0;
always @(posedge i_clk)
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]
*/
debug_trigger <= (!i_halt)&&(o_break);
 
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 [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, 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]
*/
/*
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
};
*/
 
// 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 Property changes : Added: svn:ignore ## -0,0 +1 ## +obj_dir Index: cpudefs.v =================================================================== --- cpudefs.v (revision 205) +++ cpudefs.v (revision 209) @@ -28,7 +28,7 @@ // //////////////////////////////////////////////////////////////////////////////// // -// Copyright (C) 2015-2016, Gisselquist Technology, LLC +// Copyright (C) 2015-2019, 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,22 +60,7 @@ // 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 @@ -239,6 +224,15 @@ // // // +// 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 @@ -261,7 +255,13 @@ `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: ex/busdelay.v =================================================================== --- ex/busdelay.v (nonexistent) +++ ex/busdelay.v (revision 209) @@ -0,0 +1,473 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: busdelay.v +// +// Project: Zip CPU -- a small, lightweight, RISC CPU soft core +// +// Purpose: Delay any access to the wishbone bus by a single clock. +// +// When the first Zip System would not meet the timing requirements of +// the board it was placed upon, this bus delay was added to help out. +// It may no longer be necessary, having cleaned some other problems up +// first, but it will remain here as a means of alleviating timing +// problems. +// +// The specific problem takes place on the stall line: a wishbone master +// *must* know on the first clock whether or not the bus will stall. +// +// +// After a period of time, I started a new design where the timing +// associated with this original bus clock just wasn't ... fast enough. +// I needed to delay the stall line as well. A new busdelay was then +// written and debugged whcih delays the stall line. (I know, you aren't +// supposed to delay the stall line--but what if you *have* to in order +// to meet timing?) This new logic has been merged in with the old, +// and the DELAY_STALL line can be set to non-zero to use it instead +// of the original logic. Don't use it if you don't need it: it will +// consume resources and slow your bus down more, but if you do need +// it--don't be afraid to use it. +// +// Both versions of the bus delay will maintain a single access per +// clock when pipelined, they only delay the time between the strobe +// going high and the actual command being accomplished. +// +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2015-2019, 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 +// +// +//////////////////////////////////////////////////////////////////////////////// +// +// +`default_nettype none +// +module busdelay(i_clk, i_reset, + // The input bus + i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, i_wb_sel, + o_wb_ack, o_wb_stall, o_wb_data, o_wb_err, + // The delayed bus + o_dly_cyc, o_dly_stb, o_dly_we, o_dly_addr,o_dly_data,o_dly_sel, + i_dly_ack, i_dly_stall, i_dly_data, i_dly_err); + parameter AW=32, DW=32; + localparam F_LGDEPTH=4; + parameter [0:0] DELAY_STALL = 1; + input wire i_clk, i_reset; + // Input/master bus + input wire i_wb_cyc, i_wb_stb, i_wb_we; + input wire [(AW-1):0] i_wb_addr; + input wire [(DW-1):0] i_wb_data; + input wire [(DW/8-1):0] i_wb_sel; + output reg o_wb_ack; + output wire o_wb_stall; + output reg [(DW-1):0] o_wb_data; + output reg o_wb_err; + // Delayed bus + output reg o_dly_cyc, o_dly_stb, o_dly_we; + output reg [(AW-1):0] o_dly_addr; + output reg [(DW-1):0] o_dly_data; + output reg [(DW/8-1):0] o_dly_sel; + input wire i_dly_ack; + input wire i_dly_stall; + input wire [(DW-1):0] i_dly_data; + input wire i_dly_err; + +`ifdef FORMAL + wire [2+AW+DW+DW/8-1:0] f_wpending; +`endif + + generate + if (DELAY_STALL != 0) + begin + reg r_stb, r_we; + reg [(AW-1):0] r_addr; + reg [(DW-1):0] r_data; + reg [(DW/8-1):0] r_sel; + + initial o_dly_cyc = 1'b0; + initial o_dly_stb = 1'b0; + initial o_dly_we = 1'b0; + initial o_dly_addr = 0; + initial o_dly_data = 0; + initial o_dly_sel = 0; + initial r_stb = 1'b0; + initial r_we = 1'b0; + initial r_addr = 0; + initial r_data = 0; + initial r_sel = 0; + initial o_wb_ack = 1'b0; + initial o_wb_err = 1'b0; + always @(posedge i_clk) + begin + o_dly_cyc <= (i_wb_cyc)&&(!i_reset)&&(!o_wb_err) + &&((!i_dly_err)||(!o_dly_cyc)); + + if ((!i_dly_stall)||(!o_dly_stb)) + begin + r_we <= i_wb_we; + r_addr <= i_wb_addr; + r_data <= i_wb_data; + r_sel <= i_wb_sel; + + if (r_stb) + begin + o_dly_we <= r_we; + o_dly_addr <= r_addr; + o_dly_data <= r_data; + o_dly_sel <= r_sel; + o_dly_stb <= 1'b1; + end else begin + o_dly_we <= i_wb_we; + o_dly_addr <= i_wb_addr; + o_dly_data <= i_wb_data; + o_dly_sel <= i_wb_sel; + o_dly_stb <= i_wb_stb; + end + + r_stb <= 1'b0; + end else if ((!r_stb)&&(!o_wb_stall)) + begin + r_we <= i_wb_we; + r_addr <= i_wb_addr; + r_data <= i_wb_data; + r_sel <= i_wb_sel; + r_stb <= i_wb_stb; + end + + if ((!i_wb_cyc)||((i_dly_err)&&(o_dly_cyc))||(o_wb_err)) + begin + o_dly_stb <= 1'b0; + r_stb <= 1'b0; + end + + if ((i_reset)||(!i_wb_cyc)||(o_wb_err)) + o_wb_ack <= 1'b0; + else + o_wb_ack <= (i_dly_ack)&&(o_dly_cyc)&&(!i_dly_err); + o_wb_data <= i_dly_data; + + if (!i_wb_cyc) + o_wb_err <= 1'b0; + else + o_wb_err <= (i_dly_err)&&(o_dly_cyc); + + if (i_reset) + begin + r_stb <= 0; + r_we <= 0; + o_dly_stb <= 0; + o_wb_err <= 0; + o_wb_ack <= 0; + end + end + + assign o_wb_stall = r_stb; + +`ifdef FORMAL + assign f_wpending = { r_stb, r_we, r_addr, r_data, r_sel }; +`endif + end else begin + + initial o_dly_cyc = 1'b0; + initial o_dly_stb = 1'b0; + initial o_dly_we = 1'b0; + initial o_dly_addr = 0; + initial o_dly_data = 0; + initial o_dly_sel = 0; + initial o_wb_ack = 0; + initial o_wb_err = 0; + + always @(posedge i_clk) + if (i_reset) + o_dly_cyc <= 1'b0; + else if ((i_dly_err)&&(o_dly_cyc)) + o_dly_cyc <= 1'b0; + else if ((o_wb_err)&&(i_wb_cyc)) + o_dly_cyc <= 1'b0; + else + o_dly_cyc <= i_wb_cyc; + + // Add the i_wb_cyc criteria here, so we can simplify the + // o_wb_stall criteria below, which would otherwise *and* + // these two. + always @(posedge i_clk) + if (i_reset) + o_dly_stb <= 1'b0; + else if ((i_dly_err)&&(o_dly_cyc)) + o_dly_stb <= 1'b0; + else if ((o_wb_err)&&(i_wb_cyc)) + o_dly_stb <= 1'b0; + else if (!i_wb_cyc) + o_dly_stb <= 1'b0; + else if (!o_wb_stall) + o_dly_stb <= (i_wb_stb); + + always @(posedge i_clk) + if (!o_wb_stall) + o_dly_we <= i_wb_we; + always @(posedge i_clk) + if (!o_wb_stall) + o_dly_addr<= i_wb_addr; + always @(posedge i_clk) + if (!o_wb_stall) + o_dly_data <= i_wb_data; + always @(posedge i_clk) + if (!o_wb_stall) + o_dly_sel <= i_wb_sel; + always @(posedge i_clk) + if (i_reset) + o_wb_ack <= 1'b0; + else + o_wb_ack <= ((i_dly_ack)&&(!i_dly_err) + &&(o_dly_cyc)&&(i_wb_cyc)) + &&(!o_wb_err); + always @(posedge i_clk) + if (i_reset) + o_wb_err <= 1'b0; + else if (!o_dly_cyc) + o_wb_err <= 1'b0; + else + o_wb_err <= (o_wb_err)||(i_dly_err)&&(i_wb_cyc); + always @(posedge i_clk) + o_wb_data <= i_dly_data; + + // Our only non-delayed line, yet still really delayed. Perhaps + // there's a way to register this? + // o_wb_stall <= (i_wb_cyc)&&(i_wb_stb) ... or some such? + // assign o_wb_stall=((i_wb_cyc)&&(i_dly_stall)&&(o_dly_stb));//&&o_cyc + assign o_wb_stall = (i_dly_stall)&&(o_dly_stb); + +`ifdef FORMAL + // f_wpending isn't used if DELAY_STALL is zero, but we'll give + // it a seemingly useful value anyway--if for no other reason + // than to be sure we set it to the right number of bits + assign f_wpending = { i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, i_wb_sel }; +`endif + end endgenerate + +`ifdef FORMAL + +`ifdef BUSDELAY +`define ASSUME assume +`else +`define ASSUME assert +`endif + + 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); + + wire [(F_LGDEPTH-1):0] f_wb_nreqs,f_wb_nacks, f_wb_outstanding, + f_dly_nreqs, f_dly_nacks, f_dly_outstanding; + + localparam ACK_DELAY = 5, + STALL_DELAY = 4; + fwb_slave #(.AW(AW), .DW(DW), + .F_LGDEPTH(F_LGDEPTH), + .F_MAX_STALL(STALL_DELAY+1), + .F_MAX_ACK_DELAY(ACK_DELAY+1+2*STALL_DELAY), + .F_MAX_REQUESTS((1< for a copy. +// +// License: GPL, v3, as defined and found on www.gnu.org, +// http://www.gnu.org/licenses/gpl.html +// +// +//////////////////////////////////////////////////////////////////////////////// +// +// +`default_nettype none +// +module fwb_counter(i_clk, i_reset, + // The Wishbone bus + i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, i_wb_sel, + i_wb_ack, i_wb_stall, i_wb_idata, i_wb_err, + // Some convenience output parameters + f_nreqs, f_nacks, f_outstanding); + parameter AW=32, DW=32; + parameter F_MAX_STALL = 0, + F_MAX_ACK_DELAY = 0; + parameter F_LGDEPTH = 4; + parameter [(F_LGDEPTH-1):0] F_MAX_REQUESTS = 0; + // + // If true, allow the bus to be kept open when there are no outstanding + // requests. This is useful for any master that might execute a + // read modify write cycle, such as an atomic add. + parameter [0:0] F_OPT_RMW_BUS_OPTION = 1; + // + // + // If true, allow the bus to issue multiple discontinuous requests. + // Unlike F_OPT_RMW_BUS_OPTION, these requests may be issued while other + // requests are outstanding + parameter [0:0] F_OPT_DISCONTINUOUS = 0; + // + // + // If true, insist that there be a minimum of a single clock delay + // between request and response. This defaults to off since the + // wishbone specification specifically doesn't require this. However, + // some interfaces do, so we allow it as an option here. + parameter [0:0] F_OPT_MINCLOCK_DELAY = 0; + // + // + localparam [(F_LGDEPTH-1):0] MAX_OUTSTANDING = {(F_LGDEPTH){1'b1}}; + localparam MAX_DELAY = (F_MAX_STALL > F_MAX_ACK_DELAY) + ? F_MAX_STALL : F_MAX_ACK_DELAY; + localparam DLYBITS= (MAX_DELAY < 4) ? 2 + : ((MAX_DELAY < 16) ? 4 + : ((MAX_DELAY < 64) ? 6 + : ((MAX_DELAY < 256) ? 8 + : ((MAX_DELAY < 1024) ? 10 + : ((MAX_DELAY < 4096) ? 12 + : ((MAX_DELAY < 16384) ? 14 + : ((MAX_DELAY < 65536) ? 16 + : 32))))))); + // + input wire i_clk, i_reset; + // Input/master bus + input wire i_wb_cyc, i_wb_stb, i_wb_we; + input wire [(AW-1):0] i_wb_addr; + input wire [(DW-1):0] i_wb_data; + input wire [(DW/8-1):0] i_wb_sel; + // + input wire i_wb_ack; + input wire i_wb_stall; + input wire [(DW-1):0] i_wb_idata; + input wire i_wb_err; + // + output reg [(F_LGDEPTH-1):0] f_nreqs, f_nacks; + output wire [(F_LGDEPTH-1):0] f_outstanding; + + // + // Let's just make sure our parameters are set up right + // + always @(*) + assert(F_MAX_REQUESTS < {(F_LGDEPTH){1'b1}}); + + // + // + // Bus requests + // + // + + // + // Count the number of requests that have been received + // + initial f_nreqs = 0; + always @(posedge i_clk) + if ((i_reset)||(!i_wb_cyc)) + f_nreqs <= 0; + else if ((i_wb_stb)&&(!i_wb_stall)) + f_nreqs <= f_nreqs + 1'b1; + + + // + // Count the number of acknowledgements that have been returned + // + initial f_nacks = 0; + always @(posedge i_clk) + if (i_reset) + f_nacks <= 0; + else if (!i_wb_cyc) + f_nacks <= 0; + else if ((i_wb_ack)||(i_wb_err)) + f_nacks <= f_nacks + 1'b1; + + // + // The number of outstanding requests is the difference between + // the number of requests and the number of acknowledgements + // + assign f_outstanding = (i_wb_cyc) ? (f_nreqs - f_nacks):0; + +endmodule Index: ex/fwb_master.v =================================================================== --- ex/fwb_master.v (nonexistent) +++ ex/fwb_master.v (revision 209) @@ -0,0 +1,447 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: fwb_master.v +// +// Project: Zip CPU -- a small, lightweight, RISC CPU soft core +// +// Purpose: This file describes the rules of a wishbone interaction from the +// perspective of a wishbone master. These formal rules may be used +// with yosys-smtbmc to *prove* that the master properly handles outgoing +// transactions and incoming responses. +// +// This module contains no functional logic. It is intended for formal +// verification only. The outputs returned, the number of requests that +// have been made, the number of acknowledgements received, and the number +// of outstanding requests, are designed for further formal verification +// purposes *only*. +// +// This file is different from a companion formal_slave.v file in that the +// assertions are made on the outputs of the wishbone master: o_wb_cyc, +// o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, and o_wb_sel, while only +// assumptions are made about the inputs: i_wb_stall, i_wb_ack, i_wb_data, +// i_wb_err. In the formal_slave.v, assumptions are made about the +// slave inputs (the master outputs), and assertions are made about the +// slave outputs (the master inputs). +// +// In order to make it easier to compare the slave against the master, +// assumptions with respect to the slave have been marked with the +// `SLAVE_ASSUME macro. Similarly, assertions the slave would make have +// been marked with `SLAVE_ASSERT. This allows the master to redefine +// these two macros to be from his perspective, and therefore the +// diffs between the two files actually show true differences, rather +// than just these differences in perspective. +// +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2017-2019, 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 +// +// +//////////////////////////////////////////////////////////////////////////////// +// +// +`default_nettype none +// +module fwb_master(i_clk, i_reset, + // The Wishbone bus + i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, i_wb_sel, + i_wb_ack, i_wb_stall, i_wb_idata, i_wb_err, + // Some convenience output parameters + f_nreqs, f_nacks, f_outstanding); + parameter AW=32, DW=32; + parameter F_MAX_STALL = 0, + F_MAX_ACK_DELAY = 0; + parameter F_LGDEPTH = 4; + parameter [(F_LGDEPTH-1):0] F_MAX_REQUESTS = 0; + // + // If true, allow the bus to be kept open when there are no outstanding + // requests. This is useful for any master that might execute a + // read modify write cycle, such as an atomic add. + parameter [0:0] F_OPT_RMW_BUS_OPTION = 1; + // + // + parameter [0:0] F_OPT_SHORT_CIRCUIT_PROOF = 0; + // + // If this is the source of a request, then we can assume STB and CYC + // will initially start out high. Master interfaces following the + // source on the way to the slave may not have this property + parameter [0:0] F_OPT_SOURCE = 0; + // + // If true, allow the bus to issue multiple discontinuous requests. + // Unlike F_OPT_RMW_BUS_OPTION, these requests may be issued while other + // requests are outstanding + parameter [0:0] F_OPT_DISCONTINUOUS = 0; + // + // + // If true, insist that there be a minimum of a single clock delay + // between request and response. This defaults to off since the + // wishbone specification specifically doesn't require this. However, + // some interfaces do, so we allow it as an option here. + parameter [0:0] F_OPT_MINCLOCK_DELAY = 0; + // + // + localparam [(F_LGDEPTH-1):0] MAX_OUTSTANDING = {(F_LGDEPTH){1'b1}}; + localparam MAX_DELAY = (F_MAX_STALL > F_MAX_ACK_DELAY) + ? F_MAX_STALL : F_MAX_ACK_DELAY; + localparam DLYBITS= (MAX_DELAY < 4) ? 2 + : ((MAX_DELAY < 16) ? 4 + : ((MAX_DELAY < 64) ? 6 + : ((MAX_DELAY < 256) ? 8 + : ((MAX_DELAY < 1024) ? 10 + : ((MAX_DELAY < 4096) ? 12 + : ((MAX_DELAY < 16384) ? 14 + : ((MAX_DELAY < 65536) ? 16 + : 32))))))); + // + input wire i_clk, i_reset; + // Input/master bus + input wire i_wb_cyc, i_wb_stb, i_wb_we; + input wire [(AW-1):0] i_wb_addr; + input wire [(DW-1):0] i_wb_data; + input wire [(DW/8-1):0] i_wb_sel; + // + input wire i_wb_ack; + input wire i_wb_stall; + input wire [(DW-1):0] i_wb_idata; + input wire i_wb_err; + // + output reg [(F_LGDEPTH-1):0] f_nreqs, f_nacks; + output wire [(F_LGDEPTH-1):0] f_outstanding; + +`define SLAVE_ASSUME assert +`define SLAVE_ASSERT assume + // + // Let's just make sure our parameters are set up right + // + initial assert(F_MAX_REQUESTS < {(F_LGDEPTH){1'b1}}); + + // + // Wrap the request line in a bundle. The top bit, named STB_BIT, + // is the bit indicating whether the request described by this vector + // is a valid request or not. + // + localparam STB_BIT = 2+AW+DW+DW/8-1; + wire [STB_BIT:0] f_request; + assign f_request = { i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, i_wb_sel }; + + // + // A quick register to be used later to know if the $past() operator + // will yield valid result + reg f_past_valid; + initial f_past_valid = 1'b0; + always @(posedge i_clk) + f_past_valid <= 1'b1; + always @(*) + if (!f_past_valid) + `SLAVE_ASSUME(i_reset); + // + // + // Assertions regarding the initial (and reset) state + // + // + + // + // Assume we start from a reset condition + initial assert(i_reset); + initial `SLAVE_ASSUME(!i_wb_cyc); + initial `SLAVE_ASSUME(!i_wb_stb); + // + initial `SLAVE_ASSERT(!i_wb_ack); + initial `SLAVE_ASSERT(!i_wb_err); + + always @(posedge i_clk) + if ((!f_past_valid)||($past(i_reset))) + begin + `SLAVE_ASSUME(!i_wb_cyc); + `SLAVE_ASSUME(!i_wb_stb); + // + `SLAVE_ASSERT(!i_wb_ack); + `SLAVE_ASSERT(!i_wb_err); + end + + always @(*) + if (!f_past_valid) + `SLAVE_ASSUME(!i_wb_cyc); + + // + // + // Bus requests + // + // + + // Following any bus error, the CYC line should be dropped to abort + // the transaction + always @(posedge i_clk) + if ((f_past_valid)&&($past(i_wb_err))&&($past(i_wb_cyc))) + `SLAVE_ASSUME(!i_wb_cyc); + + // STB can only be true if CYC is also true + always @(*) + if (i_wb_stb) + `SLAVE_ASSUME(i_wb_cyc); + + // If a request was both outstanding and stalled on the last clock, + // then nothing should change on this clock regarding it. + always @(posedge i_clk) + if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wb_stb)) + &&($past(i_wb_stall))&&(i_wb_cyc)) + begin + `SLAVE_ASSUME(i_wb_stb); + `SLAVE_ASSUME(i_wb_we == $past(i_wb_we)); + `SLAVE_ASSUME(i_wb_addr == $past(i_wb_addr)); + `SLAVE_ASSUME(i_wb_sel == $past(i_wb_sel)); + if (i_wb_we) + `SLAVE_ASSUME(i_wb_data == $past(i_wb_data)); + end + + // Within any series of STB/requests, the direction of the request + // may not change. + always @(posedge i_clk) + if ((f_past_valid)&&($past(i_wb_stb))&&(i_wb_stb)) + `SLAVE_ASSUME(i_wb_we == $past(i_wb_we)); + + + // Within any given bus cycle, the direction may *only* change when + // there are no further outstanding requests. + always @(posedge i_clk) + if ((f_past_valid)&&(f_outstanding > 0)) + `SLAVE_ASSUME(i_wb_we == $past(i_wb_we)); + + // Write requests must also set one (or more) of i_wb_sel + // always @(*) + // if ((i_wb_stb)&&(i_wb_we)) + // `SLAVE_ASSUME(|i_wb_sel); + + + // + // + // Bus responses + // + // + + // If CYC was low on the last clock, then both ACK and ERR should be + // low on this clock. + always @(posedge i_clk) + if ((f_past_valid)&&(!$past(i_wb_cyc))&&(!i_wb_cyc)) + begin + `SLAVE_ASSERT(!i_wb_ack); + `SLAVE_ASSERT(!i_wb_err); + // Stall may still be true--such as when we are not + // selected at some arbiter between us and the slave + end + + // + // Any time the CYC line drops, it is possible that there may be a + // remaining (registered) ACK or ERR that hasn't yet been returned. + // Restrict such out of band returns so that they are *only* returned + // if there is an outstanding operation. + always @(posedge i_clk) + if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wb_cyc))&&(!i_wb_cyc)) + begin + if (($past(f_outstanding == 0)) + &&((!$past(i_wb_stb && !i_wb_stall)) + ||($past(i_wb_ack|i_wb_err)))) + begin + `SLAVE_ASSERT(!i_wb_ack); + `SLAVE_ASSERT(!i_wb_err); + end + end + + // ACK and ERR may never both be true at the same time + always @(*) + `SLAVE_ASSERT((!i_wb_ack)||(!i_wb_err)); + + generate if (F_MAX_STALL > 0) + begin : MXSTALL + // + // Assume the slave cannnot stall for more than F_MAX_STALL + // counts. We'll count this forward any time STB and STALL + // are both true. + // + reg [(DLYBITS-1):0] f_stall_count; + + initial f_stall_count = 0; + always @(posedge i_clk) + if ((!i_reset)&&(i_wb_stb)&&(i_wb_stall)) + f_stall_count <= f_stall_count + 1'b1; + else + f_stall_count <= 0; + + always @(*) + if (i_wb_cyc) + `SLAVE_ASSERT(f_stall_count < F_MAX_STALL); + end endgenerate + + generate if (F_MAX_ACK_DELAY > 0) + begin : MXWAIT + // + // Assume the slave will respond within F_MAX_ACK_DELAY cycles, + // counted either from the end of the last request, or from the + // last ACK received + // + reg [(DLYBITS-1):0] f_ackwait_count; + + initial f_ackwait_count = 0; + always @(posedge i_clk) + if ((!i_reset)&&(i_wb_cyc)&&(!i_wb_stb) + &&(!i_wb_ack)&&(!i_wb_err) + &&(f_outstanding > 0)) + f_ackwait_count <= f_ackwait_count + 1'b1; + else + f_ackwait_count <= 0; + + always @(*) + if ((!i_reset)&&(i_wb_cyc)&&(!i_wb_stb) + &&(!i_wb_ack)&&(!i_wb_err) + &&(f_outstanding > 0)) + `SLAVE_ASSERT(f_ackwait_count < F_MAX_ACK_DELAY); + end endgenerate + + // + // Count the number of requests that have been made + // + initial f_nreqs = 0; + always @(posedge i_clk) + if ((i_reset)||(!i_wb_cyc)) + f_nreqs <= 0; + else if ((i_wb_stb)&&(!i_wb_stall)) + f_nreqs <= f_nreqs + 1'b1; + + + // + // Count the number of acknowledgements that have been received + // + initial f_nacks = 0; + always @(posedge i_clk) + if (i_reset) + f_nacks <= 0; + else if (!i_wb_cyc) + f_nacks <= 0; + else if ((i_wb_ack)||(i_wb_err)) + f_nacks <= f_nacks + 1'b1; + + // + // The number of outstanding requests is the difference between + // the number of requests and the number of acknowledgements + // + assign f_outstanding = (i_wb_cyc) ? (f_nreqs - f_nacks):0; + + always @(*) + if ((i_wb_cyc)&&(F_MAX_REQUESTS > 0)) + begin + if (i_wb_stb) + `SLAVE_ASSUME(f_nreqs < F_MAX_REQUESTS); + else + `SLAVE_ASSUME(f_nreqs <= F_MAX_REQUESTS); + `SLAVE_ASSERT(f_nacks <= f_nreqs); + assert(f_outstanding < (1< for a copy. +// +// License: GPL, v3, as defined and found on www.gnu.org, +// http://www.gnu.org/licenses/gpl.html +// +// +//////////////////////////////////////////////////////////////////////////////// +// +// +`default_nettype none +// +module fwb_slave(i_clk, i_reset, + // The Wishbone bus + i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, i_wb_sel, + i_wb_ack, i_wb_stall, i_wb_idata, i_wb_err, + // Some convenience output parameters + f_nreqs, f_nacks, f_outstanding); + parameter AW=32, DW=32; + parameter F_MAX_STALL = 0, + F_MAX_ACK_DELAY = 0; + parameter F_LGDEPTH = 4; + parameter [(F_LGDEPTH-1):0] F_MAX_REQUESTS = 0; + // + // If true, allow the bus to be kept open when there are no outstanding + // requests. This is useful for any master that might execute a + // read modify write cycle, such as an atomic add. + parameter [0:0] F_OPT_RMW_BUS_OPTION = 1; + // + // + // If true, allow the bus to issue multiple discontinuous requests. + // Unlike F_OPT_RMW_BUS_OPTION, these requests may be issued while other + // requests are outstanding + parameter [0:0] F_OPT_DISCONTINUOUS = 0; + // + // + // If true, insist that there be a minimum of a single clock delay + // between request and response. This defaults to off since the + // wishbone specification specifically doesn't require this. However, + // some interfaces do, so we allow it as an option here. + parameter [0:0] F_OPT_MINCLOCK_DELAY = 0; + // + // + localparam [(F_LGDEPTH-1):0] MAX_OUTSTANDING = {(F_LGDEPTH){1'b1}}; + localparam MAX_DELAY = (F_MAX_STALL > F_MAX_ACK_DELAY) + ? F_MAX_STALL : F_MAX_ACK_DELAY; + localparam DLYBITS= (MAX_DELAY < 4) ? 2 + : ((MAX_DELAY < 16) ? 4 + : ((MAX_DELAY < 64) ? 6 + : ((MAX_DELAY < 256) ? 8 + : ((MAX_DELAY < 1024) ? 10 + : ((MAX_DELAY < 4096) ? 12 + : ((MAX_DELAY < 16384) ? 14 + : ((MAX_DELAY < 65536) ? 16 + : 32))))))); + // + input wire i_clk, i_reset; + // Input/master bus + input wire i_wb_cyc, i_wb_stb, i_wb_we; + input wire [(AW-1):0] i_wb_addr; + input wire [(DW-1):0] i_wb_data; + input wire [(DW/8-1):0] i_wb_sel; + // + input wire i_wb_ack; + input wire i_wb_stall; + input wire [(DW-1):0] i_wb_idata; + input wire i_wb_err; + // + output reg [(F_LGDEPTH-1):0] f_nreqs, f_nacks; + output wire [(F_LGDEPTH-1):0] f_outstanding; + +`define SLAVE_ASSUME assume +`define SLAVE_ASSERT assert + // + // Let's just make sure our parameters are set up right + // + initial assert(F_MAX_REQUESTS < {(F_LGDEPTH){1'b1}}); + + // + // Wrap the request line in a bundle. The top bit, named STB_BIT, + // is the bit indicating whether the request described by this vector + // is a valid request or not. + // + localparam STB_BIT = 2+AW+DW+DW/8-1; + wire [STB_BIT:0] f_request; + assign f_request = { i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, i_wb_sel }; + + // + // A quick register to be used later to know if the $past() operator + // will yield valid result + reg f_past_valid; + initial f_past_valid = 1'b0; + always @(posedge i_clk) + f_past_valid <= 1'b1; + always @(*) + if (!f_past_valid) + `SLAVE_ASSUME(i_reset); + // + // + // Assertions regarding the initial (and reset) state + // + // + + // + // Assume we start from a reset condition + initial assert(i_reset); + initial `SLAVE_ASSUME(!i_wb_cyc); + initial `SLAVE_ASSUME(!i_wb_stb); + // + initial `SLAVE_ASSERT(!i_wb_ack); + initial `SLAVE_ASSERT(!i_wb_err); + + always @(posedge i_clk) + if ((!f_past_valid)||($past(i_reset))) + begin + `SLAVE_ASSUME(!i_wb_cyc); + `SLAVE_ASSUME(!i_wb_stb); + // + `SLAVE_ASSERT(!i_wb_ack); + `SLAVE_ASSERT(!i_wb_err); + end + + always @(*) + if (!f_past_valid) + `SLAVE_ASSUME(!i_wb_cyc); + + // + // + // Bus requests + // + // + + // Following any bus error, the CYC line should be dropped to abort + // the transaction + always @(posedge i_clk) + if ((f_past_valid)&&($past(i_wb_err))&&($past(i_wb_cyc))) + `SLAVE_ASSUME(!i_wb_cyc); + + // STB can only be true if CYC is also true + always @(*) + if (i_wb_stb) + `SLAVE_ASSUME(i_wb_cyc); + + // If a request was both outstanding and stalled on the last clock, + // then nothing should change on this clock regarding it. + always @(posedge i_clk) + if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wb_stb)) + &&($past(i_wb_stall))&&(i_wb_cyc)) + begin + `SLAVE_ASSUME(i_wb_stb); + `SLAVE_ASSUME(i_wb_we == $past(i_wb_we)); + `SLAVE_ASSUME(i_wb_addr == $past(i_wb_addr)); + `SLAVE_ASSUME(i_wb_sel == $past(i_wb_sel)); + if (i_wb_we) + `SLAVE_ASSUME(i_wb_data == $past(i_wb_data)); + end + + // Within any series of STB/requests, the direction of the request + // may not change. + always @(posedge i_clk) + if ((f_past_valid)&&($past(i_wb_stb))&&(i_wb_stb)) + `SLAVE_ASSUME(i_wb_we == $past(i_wb_we)); + + + // Within any given bus cycle, the direction may *only* change when + // there are no further outstanding requests. + always @(posedge i_clk) + if ((f_past_valid)&&(f_outstanding > 0)) + `SLAVE_ASSUME(i_wb_we == $past(i_wb_we)); + + // Write requests must also set one (or more) of i_wb_sel + // always @(*) + // if ((i_wb_stb)&&(i_wb_we)) + // `SLAVE_ASSUME(|i_wb_sel); + + + // + // + // Bus responses + // + // + + // If CYC was low on the last clock, then both ACK and ERR should be + // low on this clock. + always @(posedge i_clk) + if ((f_past_valid)&&(!$past(i_wb_cyc))&&(!i_wb_cyc)) + begin + `SLAVE_ASSERT(!i_wb_ack); + `SLAVE_ASSERT(!i_wb_err); + // Stall may still be true--such as when we are not + // selected at some arbiter between us and the slave + end + + // + // Any time the CYC line drops, it is possible that there may be a + // remaining (registered) ACK or ERR that hasn't yet been returned. + // Restrict such out of band returns so that they are *only* returned + // if there is an outstanding operation. + always @(posedge i_clk) + if ((f_past_valid)&&(!$past(i_reset))&&($past(i_wb_cyc))&&(!i_wb_cyc)) + begin + if (($past(f_outstanding == 0)) + &&((!$past(i_wb_stb && !i_wb_stall)) + ||($past(i_wb_ack|i_wb_err)))) + begin + `SLAVE_ASSERT(!i_wb_ack); + `SLAVE_ASSERT(!i_wb_err); + end + end + + // ACK and ERR may never both be true at the same time + always @(*) + `SLAVE_ASSERT((!i_wb_ack)||(!i_wb_err)); + + generate if (F_MAX_STALL > 0) + begin : MXSTALL + // + // Assume the slave cannnot stall for more than F_MAX_STALL + // counts. We'll count this forward any time STB and STALL + // are both true. + // + reg [(DLYBITS-1):0] f_stall_count; + + initial f_stall_count = 0; + always @(posedge i_clk) + if ((!i_reset)&&(i_wb_stb)&&(i_wb_stall)) + f_stall_count <= f_stall_count + 1'b1; + else + f_stall_count <= 0; + + always @(*) + if (i_wb_cyc) + `SLAVE_ASSERT(f_stall_count < F_MAX_STALL); + end endgenerate + + generate if (F_MAX_ACK_DELAY > 0) + begin : MXWAIT + // + // Assume the slave will respond within F_MAX_ACK_DELAY cycles, + // counted either from the end of the last request, or from the + // last ACK received + // + reg [(DLYBITS-1):0] f_ackwait_count; + + initial f_ackwait_count = 0; + always @(posedge i_clk) + if ((!i_reset)&&(i_wb_cyc)&&(!i_wb_stb) + &&(!i_wb_ack)&&(!i_wb_err) + &&(f_outstanding > 0)) + f_ackwait_count <= f_ackwait_count + 1'b1; + else + f_ackwait_count <= 0; + + always @(*) + if ((!i_reset)&&(i_wb_cyc)&&(!i_wb_stb) + &&(!i_wb_ack)&&(!i_wb_err) + &&(f_outstanding > 0)) + `SLAVE_ASSERT(f_ackwait_count < F_MAX_ACK_DELAY); + end endgenerate + + // + // Count the number of requests that have been received + // + initial f_nreqs = 0; + always @(posedge i_clk) + if ((i_reset)||(!i_wb_cyc)) + f_nreqs <= 0; + else if ((i_wb_stb)&&(!i_wb_stall)) + f_nreqs <= f_nreqs + 1'b1; + + + // + // Count the number of acknowledgements that have been returned + // + initial f_nacks = 0; + always @(posedge i_clk) + if (i_reset) + f_nacks <= 0; + else if (!i_wb_cyc) + f_nacks <= 0; + else if ((i_wb_ack)||(i_wb_err)) + f_nacks <= f_nacks + 1'b1; + + // + // The number of outstanding requests is the difference between + // the number of requests and the number of acknowledgements + // + assign f_outstanding = (i_wb_cyc) ? (f_nreqs - f_nacks):0; + + always @(*) + if ((i_wb_cyc)&&(F_MAX_REQUESTS > 0)) + begin + if (i_wb_stb) + `SLAVE_ASSUME(f_nreqs < F_MAX_REQUESTS); + else + `SLAVE_ASSUME(f_nreqs <= F_MAX_REQUESTS); + `SLAVE_ASSERT(f_nacks <= f_nreqs); + assert(f_outstanding < (1< for a copy. +// +// License: GPL, v3, as defined and found on www.gnu.org, +// http://www.gnu.org/licenses/gpl.html +// +// +//////////////////////////////////////////////////////////////////////////////// +// +// +`default_nettype none +// +`define WBA_ALTERNATING +// +module wbarbiter(i_clk, i_reset, + // Bus A -- the priority bus + i_a_cyc, i_a_stb, i_a_we, i_a_adr, i_a_dat, i_a_sel, + o_a_ack, o_a_stall, o_a_err, + // Bus B + i_b_cyc, i_b_stb, i_b_we, i_b_adr, i_b_dat, i_b_sel, + o_b_ack, o_b_stall, o_b_err, + // Combined/arbitrated bus + o_cyc, o_stb, o_we, o_adr, o_dat, o_sel, i_ack, i_stall, i_err +`ifdef FORMAL + , + f_a_nreqs, f_a_nacks, f_a_outstanding, + f_b_nreqs, f_b_nacks, f_b_outstanding, + f_nreqs, f_nacks, f_outstanding +`endif + ); + parameter DW=32, AW=32; + parameter SCHEME="ALTERNATING"; + parameter [0:0] OPT_ZERO_ON_IDLE = 1'b0; + parameter F_MAX_STALL = 3; + parameter F_MAX_ACK_DELAY = 3; +`ifdef FORMAL + parameter F_LGDEPTH=3; +`endif + + // + input wire i_clk, i_reset; + // Bus A + input wire i_a_cyc, i_a_stb, i_a_we; + input wire [(AW-1):0] i_a_adr; + input wire [(DW-1):0] i_a_dat; + input wire [(DW/8-1):0] i_a_sel; + output wire o_a_ack, o_a_stall, o_a_err; + // Bus B + input wire i_b_cyc, i_b_stb, i_b_we; + input wire [(AW-1):0] i_b_adr; + input wire [(DW-1):0] i_b_dat; + input wire [(DW/8-1):0] i_b_sel; + output wire o_b_ack, o_b_stall, o_b_err; + // + output wire o_cyc, o_stb, o_we; + output wire [(AW-1):0] o_adr; + output wire [(DW-1):0] o_dat; + output wire [(DW/8-1):0] o_sel; + input wire i_ack, i_stall, i_err; + // +`ifdef FORMAL + output wire [(F_LGDEPTH-1):0] f_nreqs, f_nacks, f_outstanding, + f_a_nreqs, f_a_nacks, f_a_outstanding, + f_b_nreqs, f_b_nacks, f_b_outstanding; +`endif + + // Go high immediately (new cycle) if ... + // Previous cycle was low and *someone* is requesting a bus cycle + // Go low immadiately if ... + // We were just high and the owner no longer wants the bus + // WISHBONE Spec recommends no logic between a FF and the o_cyc + // This violates that spec. (Rec 3.15, p35) + reg r_a_owner; + + assign o_cyc = (r_a_owner) ? i_a_cyc : i_b_cyc; + initial r_a_owner = 1'b1; + + generate if (SCHEME == "PRIORITY") + begin : PRI + + always @(posedge i_clk) + if (!i_b_cyc) + r_a_owner <= 1'b1; + // Allow B to set its CYC line w/o activating this + // interface + else if ((i_b_stb)&&(!i_a_cyc)) + r_a_owner <= 1'b0; + + end else if (SCHEME == "ALTERNATING") + begin : ALT + + reg last_owner; + initial last_owner = 1'b0; + always @(posedge i_clk) + if ((i_a_cyc)&&(r_a_owner)) + last_owner <= 1'b1; + else if ((i_b_cyc)&&(!r_a_owner)) + last_owner <= 1'b0; + + always @(posedge i_clk) + if ((!i_a_cyc)&&(!i_b_cyc)) + r_a_owner <= !last_owner; + else if ((r_a_owner)&&(!i_a_cyc)) + begin + + if (i_b_stb) + r_a_owner <= 1'b0; + + end else if ((!r_a_owner)&&(!i_b_cyc)) + begin + + if (i_a_stb) + r_a_owner <= 1'b1; + + end + + end else // if (SCHEME == "LAST") + begin : LST + always @(posedge i_clk) + if ((!i_a_cyc)&&(i_b_stb)) + r_a_owner <= 1'b0; + else if ((!i_b_cyc)&&(i_a_stb)) + r_a_owner <= 1'b1; + end endgenerate + + + // Realistically, if neither master owns the bus, the output is a + // don't care. Thus we trigger off whether or not 'A' owns the bus. + // If 'B' owns it all we care is that 'A' does not. Likewise, if + // neither owns the bus than the values on the various lines are + // irrelevant. + assign o_we = (r_a_owner) ? i_a_we : i_b_we; + + generate if (OPT_ZERO_ON_IDLE) + begin + // + // OPT_ZERO_ON_IDLE will use up more logic and may even slow + // down the master clock if set. However, it may also reduce + // the power used by the FPGA by preventing things from toggling + // when the bus isn't in use. The option is here because it + // also makes it a lot easier to look for when things happen + // on the bus via VERILATOR when timing and logic counts + // don't matter. + // + assign o_stb = (o_cyc)? ((r_a_owner) ? i_a_stb : i_b_stb):0; + assign o_adr = (o_stb)? ((r_a_owner) ? i_a_adr : i_b_adr):0; + assign o_dat = (o_stb)? ((r_a_owner) ? i_a_dat : i_b_dat):0; + assign o_sel = (o_stb)? ((r_a_owner) ? i_a_sel : i_b_sel):0; + assign o_a_ack = (o_cyc)&&( r_a_owner) ? i_ack : 1'b0; + assign o_b_ack = (o_cyc)&&(!r_a_owner) ? i_ack : 1'b0; + assign o_a_stall = (o_cyc)&&( r_a_owner) ? i_stall : 1'b1; + assign o_b_stall = (o_cyc)&&(!r_a_owner) ? i_stall : 1'b1; + assign o_a_err = (o_cyc)&&( r_a_owner) ? i_err : 1'b0; + assign o_b_err = (o_cyc)&&(!r_a_owner) ? i_err : 1'b0; + end else begin + + assign o_stb = (r_a_owner) ? i_a_stb : i_b_stb; + assign o_adr = (r_a_owner) ? i_a_adr : i_b_adr; + assign o_dat = (r_a_owner) ? i_a_dat : i_b_dat; + assign o_sel = (r_a_owner) ? i_a_sel : i_b_sel; + + // We cannot allow the return acknowledgement to ever go high if + // the master in question does not own the bus. Hence we force + // it low if the particular master doesn't own the bus. + assign o_a_ack = ( r_a_owner) ? i_ack : 1'b0; + assign o_b_ack = (!r_a_owner) ? i_ack : 1'b0; + + // Stall must be asserted on the same cycle the input master + // asserts the bus, if the bus isn't granted to him. + assign o_a_stall = ( r_a_owner) ? i_stall : 1'b1; + assign o_b_stall = (!r_a_owner) ? i_stall : 1'b1; + + // + // + assign o_a_err = ( r_a_owner) ? i_err : 1'b0; + assign o_b_err = (!r_a_owner) ? i_err : 1'b0; + end endgenerate + + // Make Verilator happy + // verilator lint_off UNUSED + wire unused; + assign unused = i_reset; + // verilator lint_on UNUSED + +`ifdef FORMAL + +`ifdef WBARBITER +`define ASSUME assume +`else +`define ASSUME assert +`endif + + reg f_past_valid; + initial f_past_valid = 1'b0; + always @(posedge i_clk) + f_past_valid <= 1'b1; + + initial `ASSUME(!i_a_cyc); + initial `ASSUME(!i_a_stb); + + initial `ASSUME(!i_b_cyc); + initial `ASSUME(!i_b_stb); + + initial `ASSUME(!i_ack); + initial `ASSUME(!i_err); + + always @(*) + if (!f_past_valid) + `ASSUME(i_reset); + + always @(posedge i_clk) + begin + if (o_cyc) + assert((i_a_cyc)||(i_b_cyc)); + if ((f_past_valid)&&($past(o_cyc))&&(o_cyc)) + assert($past(r_a_owner) == r_a_owner); + end + + fwb_master #(.DW(DW), .AW(AW), + .F_MAX_STALL(F_MAX_STALL), + .F_LGDEPTH(F_LGDEPTH), + .F_MAX_ACK_DELAY(F_MAX_ACK_DELAY), + .F_OPT_RMW_BUS_OPTION(1), + .F_OPT_DISCONTINUOUS(1), + .F_OPT_CLK2FFLOGIC(1'b0)) + f_wbm(i_clk, i_reset, + o_cyc, o_stb, o_we, o_adr, o_dat, o_sel, + i_ack, i_stall, 32'h0, i_err, + f_nreqs, f_nacks, f_outstanding); + + fwb_slave #(.DW(DW), .AW(AW), + .F_MAX_STALL(0), + .F_LGDEPTH(F_LGDEPTH), + .F_MAX_ACK_DELAY(0), + .F_OPT_RMW_BUS_OPTION(1), + .F_OPT_DISCONTINUOUS(1), + .F_OPT_CLK2FFLOGIC(1'b0)) + f_wba(i_clk, i_reset, + i_a_cyc, i_a_stb, i_a_we, i_a_adr, i_a_dat, i_a_sel, + o_a_ack, o_a_stall, 32'h0, o_a_err, + f_a_nreqs, f_a_nacks, f_a_outstanding); + + fwb_slave #(.DW(DW), .AW(AW), + .F_MAX_STALL(0), + .F_LGDEPTH(F_LGDEPTH), + .F_MAX_ACK_DELAY(0), + .F_OPT_RMW_BUS_OPTION(1), + .F_OPT_DISCONTINUOUS(1), + .F_OPT_CLK2FFLOGIC(1'b0)) + f_wbb(i_clk, i_reset, + i_b_cyc, i_b_stb, i_b_we, i_b_adr, i_b_dat, i_b_sel, + o_b_ack, o_b_stall, 32'h0, o_b_err, + f_b_nreqs, f_b_nacks, f_b_outstanding); + + always @(posedge i_clk) + if (r_a_owner) + begin + assert(f_b_nreqs == 0); + assert(f_b_nacks == 0); + assert(f_a_outstanding == f_outstanding); + end else begin + assert(f_a_nreqs == 0); + assert(f_a_nacks == 0); + assert(f_b_outstanding == f_outstanding); + end + + always @(posedge i_clk) + if ((f_past_valid)&&(!$past(i_reset)) + &&($past(i_a_stb))&&(!$past(i_b_cyc))) + assert(r_a_owner); + always @(posedge i_clk) + if ((f_past_valid)&&(!$past(i_reset)) + &&(!$past(i_a_cyc))&&($past(i_b_stb))) + assert(!r_a_owner); + + always @(posedge i_clk) + if ((f_past_valid)&&(r_a_owner != $past(r_a_owner))) + assert(!$past(o_cyc)); + +`endif +endmodule + Index: ex/wbdblpriarb.v =================================================================== --- ex/wbdblpriarb.v (nonexistent) +++ ex/wbdblpriarb.v (revision 209) @@ -0,0 +1,454 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: wbdblpriarb.v +// +// Project: Zip CPU -- a small, lightweight, RISC CPU soft core +// +// Purpose: This should almost be identical to the priority arbiter, save +// for a simple diffence: it allows the arbitration of two +// separate wishbone buses. The purpose of this is to push the address +// resolution back one cycle, so that by the first clock visible to this +// core, it is known which of two parts of the bus the desired address +// will be on, save that we still use the arbiter since the underlying +// device doesn't know that there are two wishbone buses. +// +// So at this point we've deviated from the WB spec somewhat, by allowing +// two CYC and two STB lines. Everything else is the same. This allows +// (in this case the Zip CPU) to determine whether or not the access +// will be to the local ZipSystem bus or the external WB bus on the clock +// before the local bus access, otherwise peripherals were needing to do +// multiple device selection comparisons/test within a clock: 1) is this +// for the local or external bus, and 2) is this referencing me as a +// peripheral. This then caused the ZipCPU to fail all timing specs. +// By creating the two pairs of lines, CYC_A/STB_A and CYC_B/STB_B, the +// determination of local vs external can be made one clock earlier +// where there's still time for the logic, and the second comparison +// now has time to complete. +// +// So let me try to explain this again. To use this arbiter, one of the +// two masters sets CYC and STB before, only the master determines which +// of two address spaces the CYC and STB apply to before the clock and +// only sets the appropriate CYC and STB lines. Then, on the clock tick, +// the arbiter determines who gets *both* busses, as they both share every +// other WB line. Thus, only one of CYC_A and CYC_B going out will ever +// be high at a given time. +// +// Hopefully this makes more sense than it sounds. If not, check out the +// code below for a better explanation. +// +// 20150919 -- Added supported for the WB error signal. +// +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2015,2018-2019, 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 +// +// +//////////////////////////////////////////////////////////////////////////////// +// +// +`default_nettype none +// +module wbdblpriarb(i_clk, i_reset, + // Bus A + i_a_cyc_a,i_a_cyc_b,i_a_stb_a,i_a_stb_b,i_a_we,i_a_adr, i_a_dat, i_a_sel, o_a_ack, o_a_stall, o_a_err, + // Bus B + i_b_cyc_a,i_b_cyc_b,i_b_stb_a,i_b_stb_b,i_b_we,i_b_adr, i_b_dat, i_b_sel, o_b_ack, o_b_stall, o_b_err, + // Both buses + o_cyc_a, o_cyc_b, o_stb_a, o_stb_b, o_we, o_adr, o_dat, o_sel, + i_ack, i_stall, i_err +`ifdef FORMAL + , f_nreqs_a, f_nacks_a, f_outstanding_a, + f_nreqs_b, f_nacks_b, f_outstanding_b, + f_a_nreqs_a, f_a_nacks_a, f_a_outstanding_a, + f_a_nreqs_b, f_a_nacks_b, f_a_outstanding_b, + f_b_nreqs_a, f_b_nacks_a, f_b_outstanding_a, + f_b_nreqs_b, f_b_nacks_b, f_b_outstanding_b +`endif + ); + + parameter DW=32, AW=32; + // + // ZERO_ON_IDLE uses more logic than the alternative. It should be + // useful for reducing power, as these circuits tend to drive wires + // all the way across the design, but it may also slow down the master + // clock. I've used it as an option when using VERILATOR, 'cause + // zeroing things on idle can make them stand out all the more when + // staring at wires and dumps and such. + parameter [0:0] OPT_ZERO_ON_IDLE = 1'b0; + // + // + parameter F_LGDEPTH = 3; + // + // + parameter F_MAX_STALL = 0; + parameter F_MAX_ACK_DELAY=0; + // + // Wishbone doesn't use an i_ce signal. While it could, they dislike + // what it would (might) do to the synchronous reset signal, i_reset. + input wire i_clk, i_reset; + // Bus A + input wire i_a_cyc_a, i_a_cyc_b, i_a_stb_a, i_a_stb_b, i_a_we; + input wire [(AW-1):0] i_a_adr; + input wire [(DW-1):0] i_a_dat; + input wire [(DW/8-1):0] i_a_sel; + output wire o_a_ack, o_a_stall, o_a_err; + // Bus B + input wire i_b_cyc_a, i_b_cyc_b, i_b_stb_a, i_b_stb_b, i_b_we; + input wire [(AW-1):0] i_b_adr; + input wire [(DW-1):0] i_b_dat; + input wire [(DW/8-1):0] i_b_sel; + output wire o_b_ack, o_b_stall, o_b_err; + // + output wire o_cyc_a,o_cyc_b, o_stb_a, o_stb_b, o_we; + output wire [(AW-1):0] o_adr; + output wire [(DW-1):0] o_dat; + output wire [(DW/8-1):0] o_sel; + input wire i_ack, i_stall, i_err; +`ifdef FORMAL + output wire [(F_LGDEPTH-1):0] + f_nreqs_a, f_nacks_a, f_outstanding_a, + f_nreqs_b, f_nacks_b, f_outstanding_b, + f_a_nreqs_a, f_a_nacks_a, f_a_outstanding_a, + f_a_nreqs_b, f_a_nacks_b, f_a_outstanding_b, + f_b_nreqs_a, f_b_nacks_a, f_b_outstanding_a, + f_b_nreqs_b, f_b_nacks_b, f_b_outstanding_b; +`endif + + // All of our logic is really captured in the 'r_a_owner' register. + // This register determines who owns the bus. If no one is requesting + // the bus, ownership goes to A on the next clock. Otherwise, if B is + // requesting the bus and A is not, then ownership goes to not A on + // the next clock. (Sounds simple ...) + // + // The CYC logic is here to make certain that, by the time we determine + // who the bus owner is, we can do so based upon determined criteria. + reg r_a_owner; + + initial r_a_owner = 1'b1; + always @(posedge i_clk) + if (i_reset) + r_a_owner <= 1'b1; + /* + // Remain with the "last owner" until 1) the other bus requests + // access, and 2) the last owner no longer wants it. This + // logic "idles" on the last owner. + // + // This is an alternating bus owner strategy + // + else if ((!o_cyc_a)&&(!o_cyc_b)) + r_a_owner <= ((i_b_stb_a)||(i_b_stb_b))? 1'b0:1'b1; + // + // Expanding this out + // + // else if ((r_a_owner)&&((i_a_cyc_a)||(i_a_cyc_b))) + // r_a_owner <= 1'b1; + // else if ((!r_a_owner)&&((i_b_cyc_a)||(i_b_cyc_b))) + // r_a_owner <= 1'b0; + // else if ((r_a_owner)&&((i_b_stb_a)||(i_b_stb_b))) + // r_a_owner <= 1'b0; + // else if ((!r_a_owner)&&((i_a_stb_a)||(i_a_stb_b))) + // r_a_owner <= 1'b0; + // + // Logic required: + // + // Reset line + // + 9 inputs (data) + // + 9 inputs (CE) + // Could be done with three LUTs + // First two evaluate o_cyc_a and o_cyc_b (above) + */ + // Option 2: + // + // "Idle" on A as the owner. + // If a request is made from B, AND A is idle, THEN + // switch. Otherwise, if B is ever idle, revert back to A + // regardless of whether A wants it or not. + else if ((!i_b_cyc_a)&&(!i_b_cyc_b)) + r_a_owner <= 1'b1; + else if ((!i_a_cyc_a)&&(!i_a_cyc_b) + &&((i_b_stb_a)||(i_b_stb_b))) + r_a_owner <= 1'b0; + + // Realistically, if neither master owns the bus, the output is a + // don't care. Thus we trigger off whether or not 'A' owns the bus. + // If 'B' owns it all we care is that 'A' does not. Likewise, if + // neither owns the bus than the values on these various lines are + // irrelevant. + + assign o_cyc_a = ((r_a_owner) ? i_a_cyc_a : i_b_cyc_a); + assign o_cyc_b = ((r_a_owner) ? i_a_cyc_b : i_b_cyc_b); + assign o_stb_a = (r_a_owner) ? i_a_stb_a : i_b_stb_a; + assign o_stb_b = (r_a_owner) ? i_a_stb_b : i_b_stb_b; + assign o_we = (r_a_owner) ? i_a_we : i_b_we; + generate if (OPT_ZERO_ON_IDLE) + begin + wire o_cyc, o_stb; + assign o_cyc = ((o_cyc_a)||(o_cyc_b)); + assign o_stb = ((o_stb_a)||(o_stb_b)); + assign o_adr = (o_stb)?((r_a_owner) ? i_a_adr : i_b_adr):0; + assign o_dat = (o_stb)?((r_a_owner) ? i_a_dat : i_b_dat):0; + assign o_sel = (o_stb)?((r_a_owner) ? i_a_sel : i_b_sel):0; + assign o_a_ack = (o_cyc)&&( r_a_owner) ? i_ack : 1'b0; + assign o_b_ack = (o_cyc)&&(!r_a_owner) ? i_ack : 1'b0; + assign o_a_stall = (o_cyc)&&( r_a_owner) ? i_stall : 1'b1; + assign o_b_stall = (o_cyc)&&(!r_a_owner) ? i_stall : 1'b1; + assign o_a_err = (o_cyc)&&( r_a_owner) ? i_err : 1'b0; + assign o_b_err = (o_cyc)&&(!r_a_owner) ? i_err : 1'b0; + end else begin + assign o_adr = (r_a_owner) ? i_a_adr : i_b_adr; + assign o_dat = (r_a_owner) ? i_a_dat : i_b_dat; + assign o_sel = (r_a_owner) ? i_a_sel : i_b_sel; + + // We cannot allow the return acknowledgement to ever go high if + // the master in question does not own the bus. Hence we force it + // low if the particular master doesn't own the bus. + assign o_a_ack = ( r_a_owner) ? i_ack : 1'b0; + assign o_b_ack = (!r_a_owner) ? i_ack : 1'b0; + + // Stall must be asserted on the same cycle the input master asserts + // the bus, if the bus isn't granted to him. + assign o_a_stall = ( r_a_owner) ? i_stall : 1'b1; + assign o_b_stall = (!r_a_owner) ? i_stall : 1'b1; + + // + // + assign o_a_err = ( r_a_owner) ? i_err : 1'b0; + assign o_b_err = (!r_a_owner) ? i_err : 1'b0; + end endgenerate + +`ifdef FORMAL +`define ASSERT assert +`ifdef WBDBLPRIARB +`define ASSUME assume +`else +`define ASSUME assert +`endif + + 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); + + initial `ASSUME(!i_a_cyc_a); + initial `ASSUME(!i_a_stb_a); + initial `ASSUME(!i_a_cyc_b); + initial `ASSUME(!i_a_stb_b); + + initial `ASSUME(!i_b_cyc_a); + initial `ASSUME(!i_b_stb_a); + initial `ASSUME(!i_b_cyc_b); + initial `ASSUME(!i_b_stb_b); + + initial `ASSUME(!i_ack); + initial `ASSUME(!i_err); + + always @(*) + `ASSUME((!i_a_cyc_a)||(!i_a_cyc_b)); + always @(*) + `ASSUME((!i_b_cyc_a)||(!i_b_cyc_b)); + + always @(posedge i_clk) + if ((f_past_valid)&&($past(i_a_cyc_a))) + `ASSUME(!i_a_cyc_b); + + always @(posedge i_clk) + if ((f_past_valid)&&($past(i_a_cyc_b))) + `ASSUME(!i_a_cyc_a); + + always @(posedge i_clk) + if ((f_past_valid)&&($past(i_b_cyc_a))) + `ASSUME(!i_b_cyc_b); + + always @(posedge i_clk) + if ((f_past_valid)&&($past(i_b_cyc_b))) + `ASSUME(!i_b_cyc_a); + + wire f_cyc, f_stb; + assign f_cyc = (o_cyc_a)||(o_cyc_b); + assign f_stb = (o_stb_a)||(o_stb_b); + always @(posedge i_clk) + begin + if (o_cyc_a) + `ASSERT((i_a_cyc_a)||(i_b_cyc_a)); + if (o_cyc_b) + `ASSERT((i_a_cyc_b)||(i_b_cyc_b)); + `ASSERT((!o_cyc_a)||(!o_cyc_b)); + end + + always @(posedge i_clk) + if ((f_past_valid)&&(!$past(i_reset))) + begin + if ($past(f_cyc)) + begin + if (($past(o_cyc_a))&&(o_cyc_a)) + `ASSERT($past(r_a_owner) == r_a_owner); + if (($past(o_cyc_b))&&(o_cyc_b)) + `ASSERT($past(r_a_owner) == r_a_owner); + end else begin + if (($past(i_a_stb_a))||($past(i_a_stb_b))) + `ASSERT(r_a_owner); + if (($past(i_b_stb_a))||($past(i_b_stb_b))) + `ASSERT(!r_a_owner); + end + end + + + fwb_master #(.AW(AW), .DW(DW), + .F_MAX_STALL(F_MAX_STALL), + .F_LGDEPTH(F_LGDEPTH), + .F_MAX_ACK_DELAY(F_MAX_ACK_DELAY), + .F_OPT_RMW_BUS_OPTION(1), + .F_OPT_DISCONTINUOUS(1)) + f_wbm_a(i_clk, i_reset, + o_cyc_a, o_stb_a, o_we, o_adr, o_dat, o_sel, + (o_cyc_a)&&(i_ack), i_stall, 32'h0, (o_cyc_a)&&(i_err), + f_nreqs_a, f_nacks_a, f_outstanding_a); + fwb_master #(.AW(AW), .DW(DW), + .F_MAX_STALL(F_MAX_STALL), + .F_MAX_ACK_DELAY(F_MAX_ACK_DELAY), + .F_LGDEPTH(F_LGDEPTH), + .F_OPT_RMW_BUS_OPTION(1), + .F_OPT_DISCONTINUOUS(1)) + f_wbm_b(i_clk, i_reset, + o_cyc_b, o_stb_b, o_we, o_adr, o_dat, o_sel, + (o_cyc_b)&&(i_ack), i_stall, 32'h0, (o_cyc_b)&&(i_err), + f_nreqs_b, f_nacks_b, f_outstanding_b); + +`ifdef WBDBLPRIARB +`define F_SLAVE fwb_slave +`else +`define F_SLAVE fwb_counter +`endif + + `F_SLAVE #(.AW(AW), .DW(DW), .F_MAX_STALL(0), + .F_LGDEPTH(F_LGDEPTH), + .F_MAX_ACK_DELAY(0), + .F_OPT_RMW_BUS_OPTION(1), + .F_OPT_DISCONTINUOUS(1)) + f_wba_a(i_clk, i_reset, + i_a_cyc_a, i_a_stb_a, i_a_we, i_a_adr, i_a_dat, i_a_sel, + (o_cyc_a)&&(o_a_ack), o_a_stall, 32'h0, (o_cyc_a)&&(o_a_err), + f_a_nreqs_a, f_a_nacks_a, f_a_outstanding_a); + `F_SLAVE #(.AW(AW), .DW(DW), .F_MAX_STALL(0), + .F_LGDEPTH(F_LGDEPTH), + .F_MAX_ACK_DELAY(0), + .F_OPT_RMW_BUS_OPTION(1), + .F_OPT_DISCONTINUOUS(1)) + f_wba_b(i_clk, i_reset, + i_a_cyc_b, i_a_stb_b, i_a_we, i_a_adr, i_a_dat, i_a_sel, + (o_cyc_b)&&(o_a_ack), o_a_stall, 32'h0, (o_cyc_b)&&(o_a_err), + f_a_nreqs_b, f_a_nacks_b, f_a_outstanding_b); + `F_SLAVE #(.AW(AW), .DW(DW), .F_MAX_STALL(0), + .F_LGDEPTH(F_LGDEPTH), + .F_MAX_ACK_DELAY(0), + .F_OPT_RMW_BUS_OPTION(1), + .F_OPT_DISCONTINUOUS(1)) + f_wbb_a(i_clk, i_reset, + i_b_cyc_a, i_b_stb_a, i_b_we, i_b_adr, i_b_dat, i_b_sel, + (o_cyc_a)&&(o_b_ack), o_b_stall, 32'h0, (o_cyc_a)&&(o_b_err), + f_b_nreqs_a, f_b_nacks_a, f_b_outstanding_a); + `F_SLAVE #(.AW(AW), .DW(DW), .F_MAX_STALL(0), + .F_LGDEPTH(F_LGDEPTH), + .F_MAX_ACK_DELAY(0), + .F_OPT_RMW_BUS_OPTION(1), + .F_OPT_DISCONTINUOUS(1)) + f_wbb_b(i_clk, i_reset, + i_b_cyc_b, i_b_stb_b, i_b_we, i_b_adr, i_b_dat, i_b_sel, + (o_cyc_b)&&(o_b_ack), o_b_stall, 32'h0, (o_cyc_b)&&(o_b_err), + f_b_nreqs_b, f_b_nacks_b, f_b_outstanding_b); + + always @(posedge i_clk) + if ((f_past_valid)&&(!$past(i_reset))) + begin + if (r_a_owner) + begin + `ASSERT(f_b_nreqs_a == 0); + `ASSERT(f_b_nreqs_b == 0); + // + `ASSERT(f_b_nacks_a == 0); + `ASSERT(f_b_nacks_b == 0); + // + if (i_a_cyc_a) + begin + `ASSERT(f_a_outstanding_a == f_outstanding_a); + `ASSERT(f_a_outstanding_b == 0); + `ASSERT(f_outstanding_b == 0); + `ASSERT(f_a_nreqs_b == 0); + `ASSERT(f_a_nacks_b == 0); + end else if (i_a_cyc_b) + begin + `ASSERT(f_a_outstanding_b == f_outstanding_b); + `ASSERT(f_a_outstanding_a == 0); + `ASSERT(f_outstanding_a == 0); + `ASSERT(f_a_nreqs_a == 0); + `ASSERT(f_a_nacks_a == 0); + end + end else begin + `ASSERT(f_a_nreqs_a == 0); + `ASSERT(f_a_nreqs_b == 0); + // + `ASSERT(f_a_nacks_a == 0); + `ASSERT(f_a_nacks_b == 0); + // + if (i_b_cyc_a) + begin + `ASSERT(f_b_outstanding_a == f_outstanding_a); + `ASSERT(f_b_outstanding_b == 0); + `ASSERT(f_outstanding_b == 0); + `ASSERT(f_b_nreqs_b == 0); + `ASSERT(f_b_nacks_b == 0); + end else if (i_b_cyc_b) + begin + `ASSERT(f_b_outstanding_b == f_outstanding_b); + `ASSERT(f_b_outstanding_a == 0); + `ASSERT(f_outstanding_a == 0); + `ASSERT(f_b_nreqs_a == 0); + `ASSERT(f_b_nacks_a == 0); + end + end + end + + always @(posedge i_clk) + if ((r_a_owner)&&(i_b_cyc_a)) + `ASSUME((i_b_stb_a)&&(!i_b_stb_b)); + always @(posedge i_clk) + if ((r_a_owner)&&(i_b_cyc_b)) + `ASSUME((i_b_stb_b)&&(!i_b_stb_a)); + + always @(posedge i_clk) + if ((!r_a_owner)&&(i_a_cyc_a)) + `ASSUME((i_a_stb_a)&&(!i_a_stb_b)); + always @(posedge i_clk) + if ((!r_a_owner)&&(i_a_cyc_b)) + `ASSUME((i_a_stb_b)&&(!i_a_stb_a)); + +`endif +endmodule + Index: ex/wbpriarbiter.v =================================================================== --- ex/wbpriarbiter.v (nonexistent) +++ ex/wbpriarbiter.v (revision 209) @@ -0,0 +1,246 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Filename: wbpriarbiter.v +// +// Project: Zip CPU -- a small, lightweight, RISC CPU soft core +// +// Purpose: This is a priority bus arbiter. It allows two separate wishbone +// masters to connect to the same bus, while also guaranteeing +// that one master can have the bus with no delay any time the other +// master is not using the bus. The goal is to eliminate the combinatorial +// logic required in the other wishbone arbiter, while still guarateeing +// access time for the priority channel. +// +// The core logic works like this: +// +// 1. When no one requests the bus, 'A' is granted the bus and guaranteed +// that any access will go right through. +// 2. If 'B' requests the bus (asserts cyc), and the bus is idle, then +// 'B' will be granted the bus. +// 3. Bus grants last as long as the 'cyc' line is high. +// 4. Once 'cyc' is dropped, the bus returns to 'A' as the owner. +// +// +// Creator: Dan Gisselquist, Ph.D. +// Gisselquist Technology, LLC +// +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright (C) 2015,2018-2019, 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 +// +// +//////////////////////////////////////////////////////////////////////////////// +// +// +`default_nettype none +// +module wbpriarbiter(i_clk, + // Bus A + i_a_cyc, i_a_stb, i_a_we, i_a_adr, i_a_dat, i_a_sel, o_a_ack, o_a_stall, o_a_err, + // Bus B + i_b_cyc, i_b_stb, i_b_we, i_b_adr, i_b_dat, i_b_sel, o_b_ack, o_b_stall, o_b_err, + // Both buses + o_cyc, o_stb, o_we, o_adr, o_dat, o_sel, i_ack, i_stall, i_err); + parameter DW=32, AW=32; + // + // ZERO_ON_IDLE uses more logic than the alternative. It should be + // useful for reducing power, as these circuits tend to drive wires + // all the way across the design, but it may also slow down the master + // clock. I've used it as an option when using VERILATOR, 'cause + // zeroing things on idle can make them stand out all the more when + // staring at wires and dumps and such. + parameter [0:0] OPT_ZERO_ON_IDLE = 1'b0; + // + input wire i_clk; + // Bus A + input wire i_a_cyc, i_a_stb, i_a_we; + input wire [(AW-1):0] i_a_adr; + input wire [(DW-1):0] i_a_dat; + input wire [(DW/8-1):0] i_a_sel; + output wire o_a_ack, o_a_stall, o_a_err; + // Bus B + input wire i_b_cyc, i_b_stb, i_b_we; + input wire [(AW-1):0] i_b_adr; + input wire [(DW-1):0] i_b_dat; + input wire [(DW/8-1):0] i_b_sel; + output wire o_b_ack, o_b_stall, o_b_err; + // + output wire o_cyc, o_stb, o_we; + output wire [(AW-1):0] o_adr; + output wire [(DW-1):0] o_dat; + output wire [(DW/8-1):0] o_sel; + input wire i_ack, i_stall, i_err; + + // Go high immediately (new cycle) if ... + // Previous cycle was low and *someone* is requesting a bus cycle + // Go low immadiately if ... + // We were just high and the owner no longer wants the bus + // WISHBONE Spec recommends no logic between a FF and the o_cyc + // This violates that spec. (Rec 3.15, p35) + reg r_a_owner; + + initial r_a_owner = 1'b1; + always @(posedge i_clk) + if (!i_b_cyc) + r_a_owner <= 1'b1; + // Allow B to set its CYC line w/o activating this interface + else if ((i_b_cyc)&&(i_b_stb)&&(!i_a_cyc)) + r_a_owner <= 1'b0; + + // Realistically, if neither master owns the bus, the output is a + // don't care. Thus we trigger off whether or not 'A' owns the bus. + // If 'B' owns it all we care is that 'A' does not. Likewise, if + // neither owns the bus than the values on these various lines are + // irrelevant. + + assign o_cyc = (r_a_owner) ? i_a_cyc : i_b_cyc; + assign o_we = (r_a_owner) ? i_a_we : i_b_we; + assign o_stb = (r_a_owner) ? i_a_stb : i_b_stb; + generate if (OPT_ZERO_ON_IDLE) + begin + assign o_adr = (o_stb)?((r_a_owner) ? i_a_adr : i_b_adr):0; + assign o_dat = (o_stb)?((r_a_owner) ? i_a_dat : i_b_dat):0; + assign o_sel = (o_stb)?((r_a_owner) ? i_a_sel : i_b_sel):0; + assign o_a_ack = (o_cyc)&&( r_a_owner) ? i_ack : 1'b0; + assign o_b_ack = (o_cyc)&&(!r_a_owner) ? i_ack : 1'b0; + assign o_a_stall = (o_cyc)&&( r_a_owner) ? i_stall : 1'b1; + assign o_b_stall = (o_cyc)&&(!r_a_owner) ? i_stall : 1'b1; + assign o_a_err = (o_cyc)&&( r_a_owner) ? i_err : 1'b0; + assign o_b_err = (o_cyc)&&(!r_a_owner) ? i_err : 1'b0; + end else begin + assign o_adr = (r_a_owner) ? i_a_adr : i_b_adr; + assign o_dat = (r_a_owner) ? i_a_dat : i_b_dat; + assign o_sel = (r_a_owner) ? i_a_sel : i_b_sel; + + // We cannot allow the return acknowledgement to ever go high if + // the master in question does not own the bus. Hence we force it + // low if the particular master doesn't own the bus. + assign o_a_ack = ( r_a_owner) ? i_ack : 1'b0; + assign o_b_ack = (!r_a_owner) ? i_ack : 1'b0; + + // Stall must be asserted on the same cycle the input master asserts + // the bus, if the bus isn't granted to him. + assign o_a_stall = ( r_a_owner) ? i_stall : 1'b1; + assign o_b_stall = (!r_a_owner) ? i_stall : 1'b1; + + // + // + assign o_a_err = ( r_a_owner) ? i_err : 1'b0; + assign o_b_err = (!r_a_owner) ? i_err : 1'b0; + end endgenerate + +`ifdef FORMAL +`ifdef WBPRIARBITER +`define ASSUME assume +`else +`define ASSUME assert +`endif + + reg f_past_valid; + initial f_past_valid = 1'b0; + always @(posedge i_clk) + f_past_valid <= 1'b1; + + initial assume(!i_a_cyc); + initial assume(!i_a_stb); + + initial assume(!i_b_cyc); + initial assume(!i_b_stb); + + initial assume(!i_ack); + initial assume(!i_err); + + always @(posedge i_clk) + begin + if (o_cyc) + assert((i_a_cyc)||(i_b_cyc)); + if ((f_past_valid)&&($past(o_cyc))&&(o_cyc)) + assert($past(r_a_owner) == r_a_owner); + if ((f_past_valid)&&($past(!o_cyc))&&($past(i_a_stb))) + assert(r_a_owner); + if ((f_past_valid)&&($past(!o_cyc))&&($past(i_b_stb))) + assert(!r_a_owner); + end + + reg f_reset; + initial f_reset = 1'b1; + always @(posedge i_clk) + f_reset <= 1'b0; + always @(*) + if (!f_past_valid) + assert(f_reset); + + parameter F_LGDEPTH=3; + + wire [(F_LGDEPTH-1):0] f_nreqs, f_nacks, f_outstanding, + f_a_nreqs, f_a_nacks, f_a_outstanding, + f_b_nreqs, f_b_nacks, f_b_outstanding; + + fwb_master #(.F_MAX_STALL(0), + .F_LGDEPTH(F_LGDEPTH), + .F_MAX_ACK_DELAY(0), + .F_OPT_RMW_BUS_OPTION(1), + .F_OPT_DISCONTINUOUS(1)) + f_wbm(i_clk, f_reset, + o_cyc, o_stb, o_we, o_adr, o_dat, o_sel, + i_ack, i_stall, 32'h0, i_err, + f_nreqs, f_nacks, f_outstanding); + fwb_slave #(.F_MAX_STALL(0), + .F_LGDEPTH(F_LGDEPTH), + .F_MAX_ACK_DELAY(0), + .F_OPT_RMW_BUS_OPTION(1), + .F_OPT_DISCONTINUOUS(1)) + f_wba(i_clk, f_reset, + i_a_cyc, i_a_stb, i_a_we, i_a_adr, i_a_dat, i_a_sel, + o_a_ack, o_a_stall, 32'h0, o_a_err, + f_a_nreqs, f_a_nacks, f_a_outstanding); + fwb_slave #(.F_MAX_STALL(0), + .F_LGDEPTH(F_LGDEPTH), + .F_MAX_ACK_DELAY(0), + .F_OPT_RMW_BUS_OPTION(1), + .F_OPT_DISCONTINUOUS(1)) + f_wbb(i_clk, f_reset, + i_b_cyc, i_b_stb, i_b_we, i_b_adr, i_b_dat, i_b_sel, + o_b_ack, o_b_stall, 32'h0, o_b_err, + f_b_nreqs, f_b_nacks, f_b_outstanding); + + always @(posedge i_clk) + if (r_a_owner) + begin + assert(f_b_nreqs == 0); + assert(f_b_nacks == 0); + assert(f_a_outstanding == f_outstanding); + end else begin + assert(f_a_nreqs == 0); + assert(f_a_nacks == 0); + assert(f_b_outstanding == f_outstanding); + end + + always @(posedge i_clk) + if ((r_a_owner)&&(i_b_cyc)) + assume(i_b_stb); + + always @(posedge i_clk) + if ((r_a_owner)&&(i_a_cyc)) + assume(i_a_stb); + +`endif +endmodule Index: peripherals/README.md =================================================================== --- peripherals/README.md (nonexistent) +++ peripherals/README.md (revision 209) @@ -0,0 +1,26 @@ +# ZipCPU Peripherals + +These are not your normal peripherals, per se, but rather peripherals that +get tightly integrated with the CPU when built with the ZipSystem. These +include: + +- [icontrol.v](./icontrol.v), an interrupt controller + +- [zipcounter.v](./zipcounter.v), a *really* simple counter, for estimating CPU performance. The counter will interrupt the CPU if/when it ever rolls over. + +- [ziptimer.v](./ziptimer.v), a similarly simple timer. It just counts down and creates an interrupt + +- [zipjiffies.v](./zipjiffies.v). Modeled after the Jiffies used within the Linux Kernel, the zipjiffies peripheral counts up one count per clock. Numbers written to it request an interrupt when the clock gets to the number written. Hence, you can get really fine grained timing control using this peripheral. + +- [wbdmac.v](./wbdmac.v), a direct memory access controller. This can be used to copy memory, or even copy memory on an interrupt. Source and destination addresses may or may not increment depending upon how the controller is set. As of today, though, this controller only handles 32-bit transfers. + +- [zipmmu.v](./zipmmu.v), an experimental MMU. Has only been tested offline. + An implementation exists which integrates this MMU, however that integration + has not been tested so there are certainly some integration bugs remaining. + +*All of these peripherals* have been formally proven. + +If you are looking for the more normal peripherals, block RAM, SDRAM, etc., +feel free to examine some of the distributions that use the ZipCPU. + + Index: peripherals/icontrol.v =================================================================== --- peripherals/icontrol.v (revision 205) +++ peripherals/icontrol.v (revision 209) @@ -52,7 +52,7 @@ // //////////////////////////////////////////////////////////////////////////////// // -// Copyright (C) 2015,2017, Gisselquist Technology, LLC +// Copyright (C) 2015,2017-2019, 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,20 +76,23 @@ //////////////////////////////////////////////////////////////////////////////// // // -module icontrol(i_clk, i_reset, i_wr, i_proc_bus, o_proc_bus, +`default_nettype none +// +module icontrol(i_clk, i_reset, i_wr, i_data, o_data, i_brd_ints, o_interrupt); - 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; + 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; 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_any, r_interrupt, r_gie; + reg r_interrupt, r_gie; + wire w_any; assign nxt_int_state = (r_int_state|i_brd_ints); initial r_int_state = 0; @@ -97,7 +100,7 @@ if (i_reset) r_int_state <= 0; else if (i_wr) - r_int_state <= nxt_int_state & (~i_proc_bus[(IUSED-1):0]); + r_int_state <= i_brd_ints | (r_int_state & (~i_data[(IUSED-1):0])); else r_int_state <= nxt_int_state; initial r_int_enable = 0; @@ -104,10 +107,10 @@ always @(posedge i_clk) if (i_reset) r_int_enable <= 0; - 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]); + 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]); initial r_gie = 1'b0; always @(posedge i_clk) @@ -114,46 +117,98 @@ if (i_reset) r_gie <= 1'b0; else if (i_wr) - r_gie <= i_proc_bus[31]; + r_gie <= i_data[BW-1]; - initial r_any = 1'b0; - always @(posedge i_clk) - r_any <= ((r_int_state & r_int_enable) != 0); + assign w_any = ((r_int_state & r_int_enable) != 0); + initial r_interrupt = 1'b0; always @(posedge i_clk) - r_interrupt <= r_gie & r_any; + if (i_reset) + r_interrupt <= 1'b0; + else + r_interrupt <= (r_gie)&&(w_any); generate if (IUSED < 15) begin - assign o_proc_bus = { + always @(posedge i_clk) + o_data <= { r_gie, { {(15-IUSED){1'b0}}, r_int_enable }, - r_any, { {(15-IUSED){1'b0}}, r_int_state } }; + w_any, { {(15-IUSED){1'b0}}, r_int_state } }; end else begin - assign o_proc_bus = { r_gie, r_int_enable, r_any, r_int_state }; + always @(posedge i_clk) + o_data <= { r_gie, r_int_enable, w_any, r_int_state }; end endgenerate - /* - reg int_condition; - initial int_condition = 1'b0; - initial o_interrupt_strobe = 1'b0; + 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; always @(posedge i_clk) - 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; - */ + f_past_valid <= 1'b1; - assign o_interrupt = r_interrupt; + 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(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,7 → 56,11
// 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)
82,7 → 86,7
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2015-2017, Gisselquist Technology, LLC
// Copyright (C) 2015-2019, 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
114,7 → 118,7
`define DMA_WRITE_REQ 3'b101
`define DMA_WRITE_ACK 3'b110
 
module wbdmac(i_clk, i_rst,
module wbdmac(i_clk, i_reset,
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,
121,13 → 125,14
i_mwb_ack, i_mwb_stall, i_mwb_data, i_mwb_err,
i_dev_ints,
o_interrupt);
parameter ADDRESS_WIDTH=32, LGMEMLEN = 10,
DW=32, LGDV=5,AW=ADDRESS_WIDTH;
input i_clk, i_rst;
parameter ADDRESS_WIDTH=30, LGMEMLEN = 10, DW=32;
localparam LGDV=5;
localparam AW=ADDRESS_WIDTH;
input wire i_clk, i_reset;
// Slave/control wishbone inputs
input i_swb_cyc, i_swb_stb, i_swb_we;
input [1:0] i_swb_addr;
input [(DW-1):0] i_swb_data;
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;
// Slave/control wishbone outputs
output reg o_swb_ack;
output wire o_swb_stall;
137,11 → 142,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 i_mwb_ack, i_mwb_stall;
input [(DW-1):0] i_mwb_data;
input i_mwb_err;
input wire i_mwb_ack, i_mwb_stall;
input wire [(DW-1):0] i_mwb_data;
input wire i_mwb_err;
// The interrupt device interrupt lines
input [(DW-1):0] i_dev_ints;
input wire [(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
153,7 → 158,42
// 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;
176,53 → 216,46
 
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)
case(dma_state)
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)
`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 ((i_swb_stb)&&(i_swb_we))
if ((s_stb)&&(s_we))
begin
case(i_swb_addr)
case(s_addr)
2'b00: begin
if ((i_swb_data[31:16] == 16'h0fed)
if ((s_data[27:16] == 12'hfed)
&&(s_data[31:30] == 2'b00)
&&(cfg_len_nonzero))
dma_state <= `DMA_WAIT;
cfg_blocklen_sub_one
<= i_swb_data[(LGMEMLEN-1):0]
<= s_data[(LGMEMLEN-1):0]
+ {(LGMEMLEN){1'b1}};
// i.e. -1;
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];
cfg_dev_trigger <= s_data[14:10];
cfg_on_dev_trigger <= s_data[15];
cfg_incs <= !s_data[29];
cfg_incd <= !s_data[28];
end
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];
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];
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)
231,13 → 264,10
dma_state <= `DMA_READ_REQ;
end
`DMA_READ_REQ: begin
nwritten <= 0;
 
if (~i_mwb_stall)
if (!i_mwb_stall)
begin
// Number of read acknowledgements needed
nracks <= nracks+1;
if (last_read_request)
if ((last_read_request)||(user_halt))
//((nracks == {1'b0, cfg_blocklen_sub_one})||(bus_nracks == cfg_len-1))
// Wishbone interruptus
dma_state <= `DMA_READ_ACK;
246,33 → 276,21
+ {{(AW-1){1'b0}},1'b1};
end
 
if (user_halt)
dma_state <= `DMA_READ_ACK;
if (i_mwb_err)
begin
cfg_len <= 0;
if ((i_mwb_err)||(abort))
dma_state <= `DMA_IDLE;
end
 
if (abort)
dma_state <= `DMA_IDLE;
if (i_mwb_ack)
else 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
nwritten <= 0;
 
if (i_mwb_err)
begin
cfg_len <= 0;
if ((i_mwb_err)||(abort))
dma_state <= `DMA_IDLE;
end else if (i_mwb_ack)
else if (i_mwb_ack)
begin
nread <= nread+1;
if (last_read_ack) // (nread+1 == nracks)
dma_state <= `DMA_PRE_WRITE;
if (user_halt)
281,8 → 299,6
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;
289,9 → 305,8
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;
304,39 → 319,20
end
end
 
if (i_mwb_err)
begin
cfg_len <= 0;
if ((i_mwb_err)||(abort))
dma_state <= `DMA_IDLE;
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)
else if ((i_mwb_ack)&&(last_write_ack))
dma_state <= (cfg_len <= 1)?`DMA_IDLE:`DMA_WAIT;
else if (!cfg_len_nonzero)
dma_state <= `DMA_IDLE;
end
`DMA_WRITE_ACK: begin
if (i_mwb_err)
begin
cfg_len <= 0;
nread <= 0;
if ((i_mwb_err)||(abort))
dma_state <= `DMA_IDLE;
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)
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)
dma_state <= `DMA_IDLE;
end
default:
345,71 → 341,150
 
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 (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;
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;
 
initial last_read_request = 1'b0;
always @(posedge i_clk)
if ((dma_state == `DMA_WAIT)||(dma_state == `DMA_READ_REQ))
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))
begin
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);
last_read_request <=
(nracks + 1 == { 1'b0, cfg_blocklen_sub_one})
||(bus_nracks == cfg_len-2);
end else
last_read_request <= 1'b0;
last_read_request <=
(nracks== { 1'b0, cfg_blocklen_sub_one})
||(bus_nracks == cfg_len-1);
end else
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 ((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;
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;
 
initial last_write_request = 1'b0;
always @(posedge i_clk)
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 (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;
 
initial last_write_ack = 1'b0;
always @(posedge i_clk)
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 (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;
 
 
assign o_mwb_cyc = (dma_state == `DMA_READ_REQ)
||(dma_state == `DMA_READ_ACK)
||(dma_state == `DMA_WRITE_REQ)
430,32 → 505,41
// 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((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 (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};
 
always @(posedge i_clk)
if ((dma_state != `DMA_WRITE_REQ)||(~i_mwb_stall))
o_mwb_data <= dma_mem[rdaddr];
if (i_reset)
o_mwb_data <= 0;
else 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)
casez(i_swb_addr)
if (i_reset)
o_swb_data <= 0;
else casez(s_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-AW){1'b0}}, cfg_raddr};
2'b11: o_swb_data <= { {(DW-AW){1'b0}}, cfg_waddr};
endcase
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
 
// 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
462,30 → 546,291
// 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)
o_swb_ack <= (i_swb_stb);
if (i_reset)
o_swb_ack <= 1'b0;
else if (!i_swb_cyc)
o_swb_ack <= 1'b0;
else
o_swb_ack <= (s_stb);
 
assign o_swb_stall = 1'b0;
 
initial abort = 1'b0;
always @(posedge i_clk)
abort <= (i_rst)||((i_swb_stb)&&(i_swb_we)
&&(i_swb_addr == 2'b00)
&&(i_swb_data == 32'hffed0000));
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));
 
initial user_halt = 1'b0;
always @(posedge i_clk)
user_halt <= ((user_halt)&&(dma_state != `DMA_IDLE))
||((i_swb_stb)&&(i_swb_we)&&(dma_state != `DMA_IDLE)
&&(i_swb_addr == 2'b00)
&&(i_swb_data == 32'hafed0000));
||((s_stb)&&(s_we)&&(dma_state != `DMA_IDLE)
&&(s_addr == 2'b00)
&&(s_data == 32'hafed0000));
 
 
// 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,32 → 6,24
//
// Purpose: A Zip timer, redesigned to be a bus watchdog
//
// 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.
// This is basically a timer, but there are some unique features to it.
//
// 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.
// 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.
//
// 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, Gisselquist Technology, LLC
// Copyright (C) 2015,2017-2019, 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
55,28 → 47,84
////////////////////////////////////////////////////////////////////////////////
//
//
module wbwatchdog(i_clk, i_rst, i_ce, i_timeout, o_int);
`default_nettype none
//
module wbwatchdog(i_clk, i_reset, i_timeout, o_int);
parameter BW = 32;
input i_clk, i_rst, i_ce;
input wire i_clk, i_reset;
// Inputs (these were at one time wishbone controlled ...)
input [(BW-1):0] i_timeout;
input wire [(BW-1):0] i_timeout;
// Interrupt line
output reg o_int;
 
reg [(BW-1):0] r_value;
initial r_value = 0;
initial r_value = {(BW){1'b1}};
always @(posedge i_clk)
if (i_rst)
if (i_reset)
r_value <= i_timeout[(BW-1):0];
else if ((i_ce)&&(~o_int))
else if (!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_rst)||(~i_ce))
if (i_reset)
o_int <= 1'b0;
else
else if (!o_int)
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-2017, Gisselquist Technology, LLC
// Copyright (C) 2015-2019, 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,15 → 51,20
////////////////////////////////////////////////////////////////////////////////
//
//
module zipcounter(i_clk, i_ce,
`default_nettype none
//
module zipcounter(i_clk, i_reset, i_event,
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 i_clk, i_ce;
//
localparam F_LGDEPTH = 2;
//
input wire i_clk, i_reset, i_event;
// Wishbone inputs
input i_wb_cyc, i_wb_stb, i_wb_we;
input [(BW-1):0] i_wb_data;
input wire i_wb_cyc, i_wb_stb, i_wb_we;
input wire [(BW-1):0] i_wb_data;
// Wishbone outputs
output reg o_wb_ack;
output wire o_wb_stall;
70,9 → 75,11
initial o_int = 0;
initial o_wb_data = 32'h00;
always @(posedge i_clk)
if ((i_wb_stb)&&(i_wb_we))
if (i_reset)
{ o_int, o_wb_data } <= 0;
else if ((i_wb_stb)&&(i_wb_we))
{ o_int, o_wb_data } <= { 1'b0, i_wb_data };
else if (i_ce)
else if (i_event)
{ o_int, o_wb_data } <= o_wb_data+{{(BW-1){1'b0}},1'b1};
else
o_int <= 1'b0;
79,6 → 86,124
 
initial o_wb_ack = 1'b0;
always @(posedge i_clk)
o_wb_ack <= (i_wb_stb);
if (i_reset)
o_wb_ack <= 1'b0;
else
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-2017, Gisselquist Technology, LLC
// Copyright (C) 2015-2019, 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,15 → 69,17
////////////////////////////////////////////////////////////////////////////////
//
//
module zipjiffies(i_clk, i_ce,
`default_nettype none
//
module zipjiffies(i_clk, i_reset, 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 i_clk, i_ce;
input wire i_clk, i_reset, i_ce;
// Wishbone inputs
input i_wb_cyc, i_wb_stb, i_wb_we;
input [(BW-1):0] i_wb_data;
input wire i_wb_cyc, i_wb_stb, i_wb_we;
input wire [(BW-1):0] i_wb_data;
// Wishbone outputs
output reg o_wb_ack;
output wire o_wb_stall;
94,8 → 96,11
// together, one clock at a time.
//
reg [(BW-1):0] r_counter;
initial r_counter = 0;
always @(posedge i_clk)
if (i_ce)
if (i_reset)
r_counter <= 0;
else if (i_ce)
r_counter <= r_counter+1;
 
//
110,9 → 115,13
 
initial new_set = 1'b0;
always @(posedge i_clk)
if (i_reset)
begin
// Delay things by a clock to simplify our logic
new_set <= ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_we));
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));
// 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;
121,7 → 130,11
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
133,18 → 146,151
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)
o_wb_ack <= (i_wb_cyc)&&(i_wb_stb);
if (i_reset)
o_wb_ack <= 1'b0;
else
o_wb_ack <= 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,19 → 103,16
// 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)
//
125,7 → 122,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,
140,10 → 137,10
// bits in the control register.
//
// +----+----+-----+----+----+----+----+--+--+--+--+
// | | 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|
// | | 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|
// +----+----+-----+----+----+----+----+--+--+--+--+
//
// +----+----+-----+----+----+----+----+--+--+--+--+
150,7 → 147,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
163,7 → 160,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.
189,7 → 186,7
//
////////////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 2016-2017, Gisselquist Technology, LLC
// Copyright (C) 2016-2019, 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
211,9 → 208,20
//
//
////////////////////////////////////////////////////////////////////////////////
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,
//
//
`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,
i_stall, i_ack, i_err, i_data,
o_rtn_stall, o_rtn_ack, o_rtn_err,
o_rtn_miss, o_rtn_data,
220,36 → 228,66
pf_return_stb, pf_return_we,
pf_return_p, pf_return_v,
pf_return_cachable);
parameter ADDRESS_WIDTH=28, LGTBL=6, PLGPGSZ=12, PLGCTXT=16, DW=32;
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;
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
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,
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),
// LGLCTX is the number of context bits in the low word
LGLCTX=((LGPGSZ-4)<LGCTXT)?(LGPGSZ-4):LGCTXT,
LGLCTX=(LGCTXT > (LGPGSZB-4))?(LGPGSZB-4):LGCTXT,
// LGHCTX is the number of context bits in the high word
LGHCTX= (LGCTXT-LGLCTX),
VAW=(DW-LGPGSZ), // Virtual address width
PAW=(AW-LGPGSZ), // Physical address width
LGHCTX= (LGCTXT-LGLCTX>0)?(LGCTXT-LGLCTX):0,
VAW=(DW-LGPGSZB), // Virtual address width, in bytes
PAW=(AW-LGPGSZW), // Physical address width, in words
TBL_BITS = LGTBL, // Bits necessary to addr tbl
TBL_SIZE=(1<<TBL_BITS);// Number of table entries
input i_clk, i_reset;
input wire i_clk, i_reset;
//
input i_ctrl_cyc_stb;
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_wbm_cyc, i_wbm_stb;
input wire i_wbm_cyc, i_wbm_stb;
//
input i_wb_we;
input [(DW-1):0] i_wb_addr;
input [(DW-1):0] i_wb_data;
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;
//
// Here's where we drive the slave side of the bus
output reg o_cyc;
256,17 → 294,18
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 i_stall, i_ack, i_err;
input [(DW-1):0] i_data;
input wire i_stall, i_ack, i_err;
input wire [(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 reg o_rtn_ack;
output wire o_rtn_ack;
output wire o_rtn_err, o_rtn_miss;
output [(DW-1):0] o_rtn_data;
output wire [(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
279,60 → 318,67
//
//
//
reg [3:1] tlb_flags [0:(TBL_SIZE-1)];
reg [3:0] tlb_flags [0:(TBL_SIZE-1)];
wire [3:0] s_tlb_flags;
reg [(LGCTXT-1):0] tlb_cdata [0:(TBL_SIZE-1)];
reg [(DW-LGPGSZ-1):0] tlb_vdata [0:(TBL_SIZE-1)];
reg [(AW-LGPGSZ-1):0] tlb_pdata [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;
 
wire adr_control, adr_status, adr_vtable, adr_ptable;
wire wr_control, wr_status, wr_vtable, wr_ptable;
wire adr_control, adr_vtable, adr_ptable;
wire wr_control, wr_vtable, wr_ptable;
wire [(LGTBL-1):0] wr_tlb_addr;
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);
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);
 
reg setup_ack, z_context, setup_this_page_flag;
reg [(DW-1):0] setup_data;
reg [(LGCTXT-1):0] r_context_word, setup_page;
reg z_context;
wire kernel_context;
reg [(LGCTXT-1):0] r_context_word;
//
wire [31:0] w_control_data,w_vtable_reg,w_ptable_reg;
wire [(LGCTXT-1):0] w_ctable_reg;
wire [31:0] w_control_data, w_ptable_reg;
reg [31:0] w_vtable_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, last_page_valid, last_ro, r_valid;
reg [(DW-1):0] r_addr;
reg r_pending, r_we, r_exe, r_valid,
last_page_valid, last_ro, last_exe;
reg [(DW-3):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;
reg [(LGTBL-1):0] s_tlb_addr, last_tlb;
reg s_tlb_miss, s_tlb_hit, s_pending;
//
wire ro_flag, simple_miss, ro_miss, table_err, cachable;
reg p_tlb_miss,p_tlb_err, pf_stb, pf_cachable;
wire ro_flag, exe_flag, simple_miss, ro_miss, exe_miss, table_err, cachable;
reg pf_stb, pf_cachable;
reg miss_pending;
//
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_ctrl_cyc_stb
// Step one -- handle the control bus--i_wbs_cyc_stb
//
//
//////////////////////////////////////////
340,23 → 386,40
begin
// Write to the Translation lookaside buffer
if (wr_vtable)
tlb_vdata[wr_tlb_addr]<=i_wb_data[(DW-1):LGPGSZ];
tlb_vdata[wr_tlb_addr]<=i_wbs_data[(DW-1):LGPGSZB];
if (wr_ptable)
tlb_pdata[wr_tlb_addr]<=i_wb_data[(AW-1):LGPGSZ];
tlb_pdata[wr_tlb_addr]<=i_wbs_data[(AW+1):LGPGSZB];
// 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_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);
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];
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;
363,48 → 426,62
always @(posedge i_clk)
if (wr_control)
begin
r_context_word <= i_wb_data[(LGCTXT-1):0];
z_context <= (i_wb_data[(LGCTXT-1):0] == {(LGCTXT){1'b0}});
r_context_word <= i_wbs_data[(LGCTXT-1):0];
z_context <= (i_wbs_data[(LGCTXT-1):0] == {(LGCTXT){1'b0}});
end
assign kernel_context = (z_context)||(!i_gie);
// Status words cannot be written to
 
/* 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 */
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 */
assign w_control_data[15: 0] = {{(16-LGCTXT){1'b0}}, r_context_word};
//
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 };
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_ptable_reg[(DW-1):LGPGSZ] = { {(DW-AW){1'b0}},
assign w_ptable_reg[(DW-1):LGPGSZB] = { {(DW-PAW-LGPGSZB){1'b0}},
tlb_pdata[wr_tlb_addr] };
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];
assign w_ptable_reg[ 3:0] = 4'h0;
//
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_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
setup_this_page_flag <= (!i_reset)&&(i_wbs_cyc_stb)&&(i_wbs_addr[LGTBL+1]);
*/
 
 
 
415,7 → 492,6
//
//
//////////////////////////////////////////
assign w_mmu_err_vaddr = { {(DW-AW){1'b0}}, r_mmu_err_vaddr };
 
//
//
424,125 → 500,280
// 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;
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))));
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));
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_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;
r_pending <= 1'b0;
s_pending <= 1'b0;
end
 
if (i_reset)
if ((!i_wbm_cyc)||(o_rtn_err)||((o_cyc)&&(i_err)))
begin
s_pending <= 1'b0;
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_BITS; k = k + 1)
for(k=0; k<TBL_SIZE; k = k + 1)
assign r_tlb_match[k] =
// The page must be valid
(tlb_valid[k])
// Virtual address must match
((tlb_vdata[k] == r_addr[(DW-1):LGPGSZ])
&&(tlb_vdata[k] == r_vpage)
// Context must match as well
&&(tlb_cdata[k] == r_context_word));
&&(tlb_cdata[k][LGCTXT-1:1] == r_context_word[LGCTXT-1:1])
&&((!tlb_cdata[k][0])||(r_context_word[0]));
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(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);
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);
s_tlb_hit <= 1'b0;
for(k=0; k<TBL_SIZE; k=k+1)
if (r_tlb_match == (1<<k))
s_tlb_hit <= (r_pending);
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);
end endgenerate
 
 
// Third clock: Read from the address the virtual table offset,
// whether read-only, etc.
assign ro_flag = tlb_flags[s_tlb_addr][3];
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 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 cachable = tlb_flags[s_tlb_addr][1];
// assign tlb_access_flag = tlb_flags[s_tlb_addr][2];
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;
 
initial pf_stb = 1'b0;
initial p_tlb_err = 1'b0;
initial p_tlb_miss = 1'b0;
initial last_ppage = 0;
initial last_vpage = 0;
always @(posedge i_clk)
if (i_reset)
begin
p_tlb_miss <= (simple_miss)||(ro_miss);
p_tlb_err <= (s_pending)&&((!s_tlb_miss)&&(!s_tlb_hit));
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;
 
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 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) };
 
if (i_reset)
last_page_valid <= 1'b0;
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);
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 <= (!i_reset)&&(i_wbm_cyc);
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_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);
rtn_err <= (i_wbm_cyc)&&(i_err)&&(o_cyc);
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)&&(((r_pending)&&(!r_valid))||(i_stall));
assign o_rtn_miss = p_tlb_miss;
assign o_rtn_err = (rtn_err)||(p_tlb_err);
assign o_rtn_stall = (i_wbm_cyc)&&(
(o_rtn_err)
||((r_pending)&&(!r_valid))
||((o_stb)&&(i_stall))
||(miss_pending));
 
assign o_addr[(AW-1):0] = {(z_context)?
r_addr[(AW-1):LGPGSZ] : last_ppage,
r_addr[(LGPGSZ-1):0]};
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_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;
551,4 → 782,378
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, Gisselquist Technology, LLC
// Copyright (C) 2015,2017-2019, 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,15 → 69,17
////////////////////////////////////////////////////////////////////////////////
//
//
module ziptimer(i_clk, i_rst, i_ce,
`default_nettype none
//
module ziptimer(i_clk, i_reset, 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 i_clk, i_rst, i_ce;
input wire i_clk, i_reset, i_ce;
// Wishbone inputs
input i_wb_cyc, i_wb_stb, i_wb_we;
input [(BW-1):0] i_wb_data;
input wire i_wb_cyc, i_wb_stb, i_wb_we;
input wire [(BW-1):0] i_wb_data;
// Wishbone outputs
output reg o_wb_ack;
output wire o_wb_stall;
88,31 → 90,34
reg r_running;
 
wire wb_write;
assign wb_write = ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_we));
assign wb_write = ((i_wb_stb)&&(i_wb_we));
 
wire auto_reload;
wire [(VW-1):0] reload_value;
wire auto_reload;
wire [(VW-1):0] interval_count;
 
initial r_running = 1'b0;
always @(posedge i_clk)
if (i_rst)
if (i_reset)
r_running <= 1'b0;
else if (wb_write)
r_running <= (|i_wb_data[(VW-1):0]);
else if ((o_int)&&(~auto_reload))
else if ((r_zero)&&(!auto_reload))
r_running <= 1'b0;
 
generate
if (RELOADABLE != 0)
begin
reg r_auto_reload;
reg [(VW-1):0] r_reload_value;
reg r_auto_reload;
reg [(VW-1):0] r_interval_count;
 
initial r_auto_reload = 1'b0;
 
always @(posedge i_clk)
if (wb_write)
r_auto_reload <= (i_wb_data[(BW-1)]);
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]);
 
assign auto_reload = r_auto_reload;
 
119,12 → 124,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)&&(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;
if (wb_write)
r_interval_count <= i_wb_data[(VW-1):0];
assign interval_count = r_interval_count;
end else begin
assign auto_reload = 1'b0;
assign reload_value = 0;
assign interval_count = 0;
end endgenerate
 
 
131,27 → 136,44
reg [(VW-1):0] r_value;
initial r_value = 0;
always @(posedge i_clk)
if (wb_write)
if (i_reset)
r_value <= 0;
else if (wb_write)
r_value <= i_wb_data[(VW-1):0];
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;
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
 
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_rst)
if ((i_reset)||(wb_write)||(!i_ce))
o_int <= 1'b0;
else if (i_ce)
o_int <= (r_running)&&(r_value == { {(VW-1){1'b0}}, 1'b1 });
else
o_int <= 1'b0;
else // if (i_ce)
o_int <= (r_value == { {(VW-1){1'b0}}, 1'b1 });
 
initial o_wb_ack = 1'b0;
always @(posedge i_clk)
o_wb_ack <= (i_wb_cyc)&&(i_wb_stb);
o_wb_ack <= (!i_reset)&&(i_wb_stb);
assign o_wb_stall = 1'b0;
 
generate
161,4 → 183,103
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, 2017, Gisselquist Technology, LLC
// Copyright (C) 2015-2019, 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,9 → 37,16
////////////////////////////////////////////////////////////////////////////////
//
//
`default_nettype none
//
`include "cpudefs.v"
//
module zipbones(i_clk, i_rst,
`define RESET_BIT 6
`define STEP_BIT 8
`define HALT_BIT 10
`define CLEAR_CACHE_BIT 11
//
module zipbones(i_clk, i_reset,
// 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,
51,46 → 58,78
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_zip_debug
, o_cpu_debug
`endif
);
parameter RESET_ADDRESS=32'h0100000, ADDRESS_WIDTH=30,
LGICACHE=8, START_HALTED=0;
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;
 
localparam AW=ADDRESS_WIDTH;
input i_clk, i_rst;
input wire i_clk, i_reset;
// Wishbone master
output wire o_wb_cyc, o_wb_stb, o_wb_we;
output wire [(AW-1):0] o_wb_addr;
output wire [(PAW-1):0] o_wb_addr;
output wire [31:0] o_wb_data;
output wire [3:0] o_wb_sel;
input i_wb_ack, i_wb_stall;
input [31:0] i_wb_data;
input i_wb_err;
input wire i_wb_ack, i_wb_stall;
input wire [31:0] i_wb_data;
input wire i_wb_err;
// Incoming interrupts
input i_ext_int;
input wire i_ext_int;
// Outgoing interrupt
output wire o_ext_int;
// Wishbone slave
input i_dbg_cyc, i_dbg_stb, i_dbg_we, i_dbg_addr;
input [31:0] i_dbg_data;
output reg o_dbg_ack;
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;
output wire o_dbg_stall;
output wire [31:0] o_dbg_data;
//
`ifdef DEBUG_SCOPE
output wire [31:0] o_zip_debug;
output wire [31:0] o_cpu_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
107,51 → 146,48
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 = (i_dbg_cyc)&&(i_dbg_stb)&&(i_dbg_we)&&(~i_dbg_addr);
assign dbg_cmd_write = (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)&&(i_dbg_data[6]));
cmd_reset <= ((dbg_cmd_write)&&(dbg_idata[`RESET_BIT]));
//
initial cmd_halt = START_HALTED;
always @(posedge i_clk)
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;
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;
 
initial cmd_clear_pf_cache = 1'b0;
initial cmd_clear_pf_cache = 1'b1;
always @(posedge i_clk)
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;
cmd_clear_pf_cache <= (dbg_cmd_write)&&(dbg_idata[`CLEAR_CACHE_BIT]);
//
initial cmd_step = 1'b0;
always @(posedge i_clk)
cmd_step <= (dbg_cmd_write)&&(i_dbg_data[8]);
cmd_step <= (dbg_cmd_write)&&(dbg_idata[`STEP_BIT]);
//
initial cmd_addr = 5'h0;
always @(posedge i_clk)
if (dbg_cmd_write)
cmd_addr <= i_dbg_data[4:0];
cmd_addr <= dbg_idata[4:0];
 
wire cpu_reset;
assign cpu_reset = (cmd_reset)||(i_rst);
assign cpu_reset = (cmd_reset);
 
wire cpu_halt, cpu_dbg_stall;
assign cpu_halt = (i_rst)||((cmd_halt)&&(~cmd_step));
assign cpu_halt = (cmd_halt);
wire [31:0] cmd_data;
// Values:
// 0x0003f -> cmd_addr mask
// 0x00040 -> reset
// 0x00080 -> PIC interrrupts enabled
// 0x00080 -> PIC interrrupt pending
// 0x00100 -> cmd_step
// 0x00200 -> cmd_stall
// 0x00400 -> cmd_halt
161,19 → 197,17
// 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,
1'b0, cpu_reset, 1'b0, cmd_addr };
1'b0, cmd_halt, (!cpu_dbg_stall), 1'b0,
i_ext_int, cpu_reset, 1'b0, cmd_addr };
 
//
// The CPU itself
//
wire cpu_gbl_stb, cpu_lcl_cyc, cpu_lcl_stb,
cpu_we, cpu_dbg_we,
wire cpu_lcl_cyc, cpu_lcl_stb,
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 = ((i_dbg_cyc)&&(i_dbg_stb)
&&(i_dbg_we)&&(i_dbg_addr));
assign cpu_dbg_we = ((dbg_stb)&&(dbg_we)&&(dbg_addr));
zipcpu #(.RESET_ADDRESS(RESET_ADDRESS),
.ADDRESS_WIDTH(ADDRESS_WIDTH),
.LGICACHE(LGICACHE),
180,7 → 214,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,
i_dbg_data, cpu_dbg_stall, cpu_dbg_data,
dbg_idata, cpu_dbg_stall, cpu_dbg_data,
cpu_dbg_cc, cpu_break,
o_wb_cyc, o_wb_stb,
cpu_lcl_cyc, cpu_lcl_stb,
189,17 → 223,23
(i_wb_err)||(cpu_lcl_cyc),
cpu_op_stall, cpu_pf_stall, cpu_i_count
`ifdef DEBUG_SCOPE
, o_zip_debug
, o_cpu_debug
`endif
);
 
// Return debug response values
assign o_dbg_data = (~i_dbg_addr)?cmd_data :cpu_dbg_data;
initial o_dbg_ack = 1'b0;
assign dbg_odata = (!dbg_addr)?cmd_data :cpu_dbg_data;
initial dbg_ack = 1'b0;
always @(posedge i_clk)
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);
dbg_ack <= (dbg_stb)&&(!o_dbg_stall);
assign dbg_stall= (cpu_dbg_stall)&&(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,11 → 6,9
//
// 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
21,21 → 19,6
// 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
46,25 → 29,41
// interrupt/time-out line is wired to the reset line instead of
// the interrupt line of the CPU.
//
// 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
// 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.)
//
// (Potentially an eventual floating point co-processor ...?)
//
// 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,
// Busses: The ZipSystem implements a series of busses to make this take
// place. These busses are identified by their prefix:
//
// 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-2017, Gisselquist Technology, LLC
// Copyright (C) 2015-2019, 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
88,23 → 87,20
////////////////////////////////////////////////////////////////////////////////
//
//
`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 // Required no longer!
// `define DELAY_EXT_BUS
//
//
// If space is tight, you might not wish to have your performance and
118,27 → 114,31
//
// Now, where am I placing all of my peripherals?
`define PERIPHBASE 32'hc0000000
`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]
`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]
 
 
`ifdef INCLUDE_ACCOUNTING_COUNTERS
`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
`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
`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_rst,
module zipsystem(i_clk, i_reset,
// 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,13 → 176,17
, o_cpu_debug
`endif
);
parameter RESET_ADDRESS=32'h0100000, ADDRESS_WIDTH=30,
LGICACHE=10, START_HALTED=1, EXTERNAL_INTERRUPTS=1,
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,
`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
195,23 → 199,33
`endif
IMPLEMENT_LOCK=1;
localparam // Derived parameters
AW=ADDRESS_WIDTH;
input i_clk, i_rst;
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;
// Wishbone master
output wire o_wb_cyc, o_wb_stb, o_wb_we;
output wire [(AW-1):0] o_wb_addr;
output wire [(PAW-1):0] o_wb_addr;
output wire [31:0] o_wb_data;
output wire [3:0] o_wb_sel;
input i_wb_ack, i_wb_stall;
input [31:0] i_wb_data;
input i_wb_err;
input wire i_wb_ack, i_wb_stall;
input wire [31:0] i_wb_data;
input wire i_wb_err;
// Incoming interrupts
input [(EXTERNAL_INTERRUPTS-1):0] i_ext_int;
input wire [(EXTERNAL_INTERRUPTS-1):0] i_ext_int;
// Outgoing interrupt
output wire o_ext_int;
// Wishbone slave
input i_dbg_cyc, i_dbg_stb, i_dbg_we, i_dbg_addr;
input [31:0] i_dbg_data;
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;
output wire o_dbg_stall;
output wire [31:0] o_dbg_data;
249,11 → 263,19
`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
264,11 → 286,15
wire dbg_cyc, dbg_stb, dbg_we, dbg_addr, dbg_stall;
wire [31:0] dbg_idata, dbg_odata;
reg dbg_ack;
wire [3:0] dbg_sel;
wire no_dbg_err;
`ifdef DELAY_DBG_BUS
wire dbg_err, no_dbg_err;
wire [3:0] dbg_sel;
// 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,
busdelay #(1,32) wbdelay(i_clk, i_reset,
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,
282,6 → 308,8
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
 
//
288,12 → 316,26
//
//
wire sys_cyc, sys_stb, sys_we;
wire [4:0] sys_addr;
wire [(AW-1):0] cpu_addr;
wire [7:0] sys_addr;
wire [(PAW-1):0] cpu_addr;
wire [31:0] sys_data;
wire sys_ack, sys_stall;
reg [31:0] sys_idata;
reg sys_ack;
wire 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
310,39 → 352,44
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_cyc)&&(dbg_stb)&&(dbg_we)&&(~dbg_addr);
assign dbg_cmd_write = (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[6]));
cmd_reset <= ((dbg_cmd_write)&&(dbg_idata[`RESET_BIT]))
||(wdt_reset);
//
initial cmd_halt = START_HALTED;
always @(posedge i_clk)
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;
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;
 
initial cmd_clear_pf_cache = 1'b1;
always @(posedge i_clk)
cmd_clear_pf_cache = (~i_rst)&&(dbg_cmd_write)
&&((dbg_idata[11])||(dbg_idata[6]));
cmd_clear_pf_cache <= (dbg_cmd_write)&&(dbg_idata[`CLEAR_CACHE_BIT]);
//
initial cmd_step = 1'b0;
always @(posedge i_clk)
cmd_step <= (dbg_cmd_write)&&(dbg_idata[8]);
cmd_step <= (dbg_cmd_write)&&(dbg_idata[`STEP_BIT]);
//
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)||(wdt_reset)||(i_rst);
assign cpu_reset = (cmd_reset);
 
wire cpu_halt, cpu_dbg_stall;
assign cpu_halt = (i_rst)||((cmd_halt)&&(~cmd_step));
assign cpu_halt = (cmd_halt);
wire [31:0] pic_data;
wire [31:0] cmd_data;
// Values:
362,11 → 409,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
 
379,8 → 426,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)&&(sys_addr == `WATCHDOG)), sys_we,
watchdog(i_clk, cpu_reset, !cmd_halt,
sys_cyc, (sys_stb)&&(sel_watchdog), sys_we,
sys_data,
wdt_ack, wdt_stall, wdt_data, wdt_reset);
 
391,21 → 438,20
// take less than the number written to this register.
//
reg wdbus_ack;
reg [(AW-1):0] r_wdbus_data;
reg [(PAW-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),
o_wb_cyc, 14'h2000, wdbus_int);
14'h2000, wdbus_int);
initial r_wdbus_data = 0;
always @(posedge i_clk)
if ((wdbus_int)||(cpu_ext_err))
r_wdbus_data = o_wb_addr;
assign wdbus_data = { {(32-AW){1'b0}}, r_wdbus_data };
if ((wdbus_int)||(cpu_err))
r_wdbus_data <= o_wb_addr;
assign wdbus_data = { {(32-PAW){1'b0}}, r_wdbus_data };
initial wdbus_ack = 1'b0;
always @(posedge i_clk)
wdbus_ack <= ((sys_cyc)&&(sys_stb)&&(sys_addr == 5'h02));
wdbus_ack <= ((sys_cyc)&&(sys_stb)&&(sel_bus_watchdog));
 
// Counters -- for performance measurement and accounting
//
421,8 → 467,8
// Master task counter
wire mtc_ack, mtc_stall;
wire [31:0] mtc_data;
zipcounter mtask_ctr(i_clk, (~cpu_halt), sys_cyc,
(sys_stb)&&(sys_addr == `MSTR_TASK_CTR),
zipcounter mtask_ctr(i_clk, 1'b0, (!cpu_halt), sys_cyc,
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b000),
sys_we, sys_data,
mtc_ack, mtc_stall, mtc_data, mtc_int);
 
429,8 → 475,8
// Master Operand Stall counter
wire moc_ack, moc_stall;
wire [31:0] moc_data;
zipcounter mmstall_ctr(i_clk,(cpu_op_stall), sys_cyc,
(sys_stb)&&(sys_addr == `MSTR_MSTL_CTR),
zipcounter mmstall_ctr(i_clk,1'b0, (cpu_op_stall), sys_cyc,
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b001),
sys_we, sys_data,
moc_ack, moc_stall, moc_data, moc_int);
 
437,8 → 483,8
// Master PreFetch-Stall counter
wire mpc_ack, mpc_stall;
wire [31:0] mpc_data;
zipcounter mpstall_ctr(i_clk,(cpu_pf_stall), sys_cyc,
(sys_stb)&&(sys_addr == `MSTR_PSTL_CTR),
zipcounter mpstall_ctr(i_clk,1'b0, (cpu_pf_stall), sys_cyc,
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b010),
sys_we, sys_data,
mpc_ack, mpc_stall, mpc_data, mpc_int);
 
445,8 → 491,8
// Master Instruction counter
wire mic_ack, mic_stall;
wire [31:0] mic_data;
zipcounter mins_ctr(i_clk,(cpu_i_count), sys_cyc,
(sys_stb)&&(sys_addr == `MSTR_INST_CTR),
zipcounter mins_ctr(i_clk,1'b0, (cpu_i_count), sys_cyc,
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b011),
sys_we, sys_data,
mic_ack, mic_stall, mic_data, mic_int);
 
457,8 → 503,8
// User task counter
wire utc_ack, utc_stall;
wire [31:0] utc_data;
zipcounter utask_ctr(i_clk,(~cpu_halt)&&(cpu_gie), sys_cyc,
(sys_stb)&&(sys_addr == `USER_TASK_CTR),
zipcounter utask_ctr(i_clk,1'b0, (!cpu_halt)&&(cpu_gie), sys_cyc,
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b100),
sys_we, sys_data,
utc_ack, utc_stall, utc_data, utc_int);
 
465,8 → 511,8
// User Op-Stall counter
wire uoc_ack, uoc_stall;
wire [31:0] uoc_data;
zipcounter umstall_ctr(i_clk,(cpu_op_stall)&&(cpu_gie), sys_cyc,
(sys_stb)&&(sys_addr == `USER_MSTL_CTR),
zipcounter umstall_ctr(i_clk,1'b0, (cpu_op_stall)&&(cpu_gie), sys_cyc,
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b101),
sys_we, sys_data,
uoc_ack, uoc_stall, uoc_data, uoc_int);
 
473,8 → 519,8
// User PreFetch-Stall counter
wire upc_ack, upc_stall;
wire [31:0] upc_data;
zipcounter upstall_ctr(i_clk,(cpu_pf_stall)&&(cpu_gie), sys_cyc,
(sys_stb)&&(sys_addr == `USER_PSTL_CTR),
zipcounter upstall_ctr(i_clk,1'b0, (cpu_pf_stall)&&(cpu_gie), sys_cyc,
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b110),
sys_we, sys_data,
upc_ack, upc_stall, upc_data, upc_int);
 
481,8 → 527,8
// User instruction counter
wire uic_ack, uic_stall;
wire [31:0] uic_data;
zipcounter uins_ctr(i_clk,(cpu_i_count)&&(cpu_gie), sys_cyc,
(sys_stb)&&(sys_addr == `USER_INST_CTR),
zipcounter uins_ctr(i_clk,1'b0, (cpu_i_count)&&(cpu_gie), sys_cyc,
(sys_stb)&&(sel_counter)&&(sys_addr[2:0] == 3'b111),
sys_we, sys_data,
uic_ack, uic_stall, uic_data, uic_int);
 
518,7 → 564,7
assign uic_int = 1'b0;
 
always @(posedge i_clk)
actr_ack <= (sys_stb)&&(sys_addr[4:3] == 2'b01);
actr_ack <= sel_counter;
`endif // INCLUDE_ACCOUNTING_COUNTERS
 
//
529,14 → 575,14
wire dmac_ack, dmac_stall;
wire dc_cyc, dc_stb, dc_we, dc_ack, dc_stall;
wire [31:0] dc_data;
wire [(AW-1):0] dc_addr;
wire [(PAW-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)&&(sys_addr[4]);
assign dmac_stb = (sys_stb)&&(sel_dmac);
`ifdef INCLUDE_DMA_CONTROLLER
wbdmac #(AW) dma_controller(i_clk, cpu_reset,
wbdmac #(PAW) dma_controller(i_clk, cpu_reset,
sys_cyc, dmac_stb, sys_we,
sys_addr[1:0], sys_data,
dmac_ack, dmac_stall, dmac_data,
549,6 → 595,7
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;
558,7 → 605,7
assign dc_cyc = 1'b0;
assign dc_stb = 1'b0;
assign dc_we = 1'b0;
assign dc_addr = { (AW) {1'b0} };
assign dc_addr = { (PAW) {1'b0} };
assign dc_data = 32'h00;
 
assign dmac_int = 1'b0;
567,21 → 614,21
wire ctri_sel, ctri_stall;
reg ctri_ack;
wire [31:0] ctri_data;
assign ctri_sel = (sys_stb)&&(sys_addr == `CTRINT);
assign ctri_sel = (sys_stb)&&(sel_apic);
initial ctri_ack = 1'b0;
always @(posedge i_clk)
ctri_ack <= ctri_sel;
ctri_ack <= (ctri_sel)&&(!cpu_reset);
assign ctri_stall = 1'b0;
`ifdef INCLUDE_ACCOUNTING_COUNTERS
//
// Counter Interrupt controller
//
generate
if (EXTERNAL_INTERRUPTS <= 9)
begin
generate if (EXTERNAL_INTERRUPTS <= 9)
begin : ALT_PIC
icontrol #(8) ctri(i_clk, cpu_reset, (ctri_sel),
sys_data, ctri_data, alt_int_vector[7:0],
ctri_int);
end else begin
end else begin : ALT_PIC
icontrol #(8+(EXTERNAL_INTERRUPTS-9))
ctri(i_clk, cpu_reset, (ctri_sel),
sys_data, ctri_data,
591,13 → 638,12
 
`else // INCLUDE_ACCOUNTING_COUNTERS
 
generate
if (EXTERNAL_INTERRUPTS <= 9)
begin
generate if (EXTERNAL_INTERRUPTS <= 9)
begin : ALT_PIC
assign ctri_stall = 1'b0;
assign ctri_data = 32'h0000;
assign ctri_int = 1'b0;
end else begin
end else begin : ALT_PIC
icontrol #(EXTERNAL_INTERRUPTS-9)
ctri(i_clk, cpu_reset, (ctri_sel),
sys_data, ctri_data,
612,10 → 658,10
//
wire tma_ack, tma_stall;
wire [31:0] tma_data;
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);
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);
 
//
// Timer B
622,10 → 668,10
//
wire tmb_ack, tmb_stall;
wire [31:0] tmb_data;
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);
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);
 
//
// Timer C
632,10 → 678,10
//
wire tmc_ack, tmc_stall;
wire [31:0] tmc_data;
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);
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);
 
//
// JIFFIES
642,8 → 688,8
//
wire jif_ack, jif_stall;
wire [31:0] jif_data;
zipjiffies jiffies(i_clk, ~cmd_halt,
sys_cyc, (sys_stb)&&(sys_addr == `JIFFIES), sys_we,
zipjiffies jiffies(i_clk, cpu_reset, !cmd_halt,
sys_cyc, (sys_stb)&&(sel_timer)&&(sys_addr[1:0] == 2'b11), sys_we,
sys_data,
jif_ack, jif_stall, jif_data, jif_int);
 
651,18 → 697,17
// The programmable interrupt controller peripheral
//
wire pic_interrupt;
generate
if (EXTERNAL_INTERRUPTS < 9)
begin
generate if (EXTERNAL_INTERRUPTS < 9)
begin : MAIN_PIC
icontrol #(6+EXTERNAL_INTERRUPTS) pic(i_clk, cpu_reset,
(sys_cyc)&&(sys_stb)&&(sys_we)
&&(sys_addr==`INTCTRL),
&&(sel_pic),
sys_data, pic_data,
main_int_vector[(6+EXTERNAL_INTERRUPTS-1):0], pic_interrupt);
end else begin
end else begin : MAIN_PIC
icontrol #(15) pic(i_clk, cpu_reset,
(sys_cyc)&&(sys_stb)&&(sys_we)
&&(sys_addr==`INTCTRL),
&&(sel_pic),
sys_data, pic_data,
main_int_vector[14:0], pic_interrupt);
end endgenerate
671,7 → 716,7
assign pic_stall = 1'b0;
reg pic_ack;
always @(posedge i_clk)
pic_ack <= (sys_stb)&&(sys_addr == `INTCTRL);
pic_ack <= (sys_stb)&&(sel_pic);
 
//
// The CPU itself
678,20 → 723,21
//
wire cpu_gbl_stb, cpu_lcl_cyc, cpu_lcl_stb,
cpu_we, cpu_dbg_we;
wire [31:0] cpu_data, wb_data;
wire [3:0] cpu_sel;
wire [31:0] cpu_data, cpu_idata;
wire [3:0] cpu_sel, mmu_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(ADDRESS_WIDTH),
zipcpu #( .RESET_ADDRESS(RESET_ADDRESS),
.ADDRESS_WIDTH(VIRTUAL_ADDRESS_WIDTH),
.LGICACHE(LGICACHE),
.OPT_LGDCACHE(LGDCACHE),
.IMPLEMENT_MPY(IMPLEMENT_MPY),
.IMPLEMENT_DIVIDE(IMPLEMENT_DIVIDE),
.IMPLEMENT_FPU(IMPLEMENT_FPU),
.IMPLEMENT_LOCK(IMPLEMENT_LOCK)
.IMPLEMENT_LOCK(IMPLEMENT_LOCK),
.WITH_LOCAL_BUS(1'b1)
)
thecpu(i_clk, cpu_reset, pic_interrupt,
cpu_halt, cmd_clear_pf_cache, cmd_addr[4:0], cpu_dbg_we,
700,8 → 746,8
cpu_gbl_cyc, cpu_gbl_stb,
cpu_lcl_cyc, cpu_lcl_stb,
cpu_we, cpu_addr, cpu_data, cpu_sel,
cpu_ack, cpu_stall, wb_data,
cpu_err,
// Return values from the Wishbone bus
cpu_ack, cpu_stall, cpu_idata, cpu_err,
cpu_op_stall, cpu_pf_stall, cpu_i_count
`ifdef DEBUG_SCOPE
, o_cpu_debug
708,24 → 754,112
`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))
&&(((cpu_halt)&&(~cpu_dbg_stall))
||(~cmd_addr[5]));
wire sys_dbg_cyc = ((dbg_cyc)&&(!cpu_lcl_cyc)&&(dbg_addr))
&&(cmd_addr[5]);
assign sys_cyc = (cpu_lcl_cyc)||(sys_dbg_cyc);
assign sys_stb = (cpu_lcl_cyc)
? (cpu_lcl_stb)
732,16 → 866,23
: ((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[4:0] : cmd_addr[4:0];
assign sys_addr= (cpu_lcl_cyc) ? cpu_addr[7:0] : { 3'h0, cmd_addr[4:0]};
assign sys_data= (cpu_lcl_cyc) ? cpu_data : dbg_idata;
 
// Return debug response values
assign dbg_odata = (~dbg_addr)?cmd_data
:((~cmd_addr[5])?cpu_dbg_data : wb_data);
// 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);
initial dbg_ack = 1'b0;
always @(posedge i_clk)
dbg_ack <= (dbg_cyc)&&(dbg_stb)&&(~dbg_stall);
assign dbg_stall=(dbg_cyc)&&((~sys_dbg_cyc)||(sys_stall))&&(dbg_addr);
dbg_ack <= (dbg_stb)&&(!dbg_stall);
assign dbg_stall=(dbg_cyc)&&(
((!sys_dbg_cyc)&&(cpu_dbg_stall))
||(sys_stall)
)&&(dbg_addr);
 
// Now for the external wishbone bus
// Need to arbitrate between the flash cache and the CPU
749,22 → 890,32
// 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 cpu_ext_ack, cpu_ext_stall, ext_ack, ext_stall,
cpu_ext_err;
wire [(AW-1):0] ext_addr;
wire [(PAW-1):0] ext_addr;
wire [31:0] ext_odata;
wire [3:0] ext_sel;
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,
wbpriarbiter #(32,PAW) dmacvcpu(i_clk,
mmu_cyc, mmu_stb, mmu_we, mmu_addr, mmu_data, mmu_sel,
mmu_ack, mmu_stall, mmu_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,32) extbus(i_clk,
ext_cyc, ext_stb, ext_we, ext_addr, ext_odata,
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,
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));
788,19 → 939,48
:(tmb_ack ? tmb_data
:(tmc_ack ? tmc_data
:jif_data));
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)
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
 
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); // 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);
| pic_stall | dmac_stall | mmus_stall); // Always 1'b0!
 
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

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.