UberDDR3/delete_later/rtl/cpu/zipbones.v

682 lines
18 KiB
Verilog

////////////////////////////////////////////////////////////////////////////////
//
// Filename: zipbones.v
// {{{
// Project: 10Gb Ethernet switch
//
// Purpose: In the spirit of keeping the Zip CPU small, this implements a
// Zip System with no peripherals: Any peripherals you wish will
// need to be implemented off-module.
//
// Creator: Dan Gisselquist, Ph.D.
// Gisselquist Technology, LLC
//
////////////////////////////////////////////////////////////////////////////////
// }}}
// Copyright (C) 2023, Gisselquist Technology, LLC
// {{{
// This file is part of the ETH10G project.
//
// The ETH10G project contains free software and gateware, licensed under the
// Apache License, Version 2.0 (the "License"). You may not use this project,
// or this file, except in compliance with the License. You may obtain a copy
// of the License at
// }}}
// http://www.apache.org/licenses/LICENSE-2.0
// {{{
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
//
////////////////////////////////////////////////////////////////////////////////
//
`default_nettype none
// }}}
module zipbones #(
// {{{
parameter RESET_ADDRESS=32'h1000_0000,
ADDRESS_WIDTH=32,
parameter BUS_WIDTH=32, // Bus data width
// CPU options
// {{{
parameter [0:0] OPT_PIPELINED=1,
parameter [0:0] OPT_EARLY_BRANCHING=OPT_PIPELINED,
// OPT_LGICACHE
// {{{
parameter OPT_LGICACHE = 2,
// }}}
// OPT_LGDCACHE
// {{{
// Set to zero for no data cache
parameter OPT_LGDCACHE = 0,
// }}}
parameter [0:0] START_HALTED=1,
parameter [0:0] OPT_DISTRIBUTED_REGS=1,
// OPT_MPY
// {{{
parameter OPT_MPY = 3,
// }}}
// OPT_DIV
// {{{
parameter [0:0] OPT_DIV=1,
// }}}
// OPT_SHIFTS
// {{{
parameter [0:0] OPT_SHIFTS = 1,
// }}}
// OPT_FPU
// {{{
parameter [0:0] OPT_FPU = 0,
// }}}
parameter [0:0] OPT_CIS=1,
parameter [0:0] OPT_LOCK=1,
parameter [0:0] OPT_USERMODE=1,
parameter [0:0] OPT_DBGPORT=START_HALTED,
parameter [0:0] OPT_TRACE_PORT=1,
parameter [0:0] OPT_PROFILER=0,
parameter [0:0] OPT_LOWPOWER=0,
`ifdef VERILATOR
parameter [0:0] OPT_SIM=1'b1,
parameter [0:0] OPT_CLKGATE = OPT_LOWPOWER,
`else
parameter [0:0] OPT_SIM=1'b0,
parameter [0:0] OPT_CLKGATE = 1'b0,
`endif
// }}}
parameter RESET_DURATION = 10,
// Short-cut names
// {{{
// localparam AW=ADDRESS_WIDTH,
localparam DBG_WIDTH=32, // Debug bus data width
localparam // Derived parameters
// PHYSICAL_ADDRESS_WIDTH=ADDRESS_WIDTH,
PAW=ADDRESS_WIDTH-$clog2(BUS_WIDTH/8)
`ifdef OPT_MMU
// VIRTUAL_ADDRESS_WIDTH=30,
`else
// VIRTUAL_ADDRESS_WIDTH=PAW,
`endif
// LGTLBSZ = 6,
// VAW=VIRTUAL_ADDRESS_WIDTH,
// }}}
// }}}
) (
// {{{
input wire i_clk, i_reset,
// Wishbone master interface from the CPU
// {{{
output wire o_wb_cyc, o_wb_stb, o_wb_we,
output wire [PAW-1:0] o_wb_addr,
output wire [BUS_WIDTH-1:0] o_wb_data,
output wire [BUS_WIDTH/8-1:0] o_wb_sel,
input wire i_wb_stall, i_wb_ack,
input wire [BUS_WIDTH-1:0] i_wb_data,
input wire i_wb_err,
// }}}
// Incoming interrupts
input wire i_ext_int,
// Our one outgoing interrupt
output wire o_ext_int,
// Wishbone slave interface for debugging purposes
// {{{
input wire i_dbg_cyc, i_dbg_stb, i_dbg_we,
input wire [5:0] i_dbg_addr,
input wire [DBG_WIDTH-1:0] i_dbg_data,
input wire [DBG_WIDTH/8-1:0] i_dbg_sel,
output wire o_dbg_stall,
output wire o_dbg_ack,
output wire [DBG_WIDTH-1:0] o_dbg_data,
// }}}
output wire [31:0] o_cpu_debug,
//
output wire o_prof_stb,
output wire [ADDRESS_WIDTH-1:0] o_prof_addr,
output wire [31:0] o_prof_ticks
// }}}
);
// Declarations
// {{{
localparam [0:0] DBG_ADDR_CTRL = 1'b0,
DBG_ADDR_CPU = 1'b1;
// Debug bit allocations
// {{{
// DBGCTRL
// 5 DBG Catch -- Catch exceptions/fautls w/ debugger
// 4 Clear cache
// 3 RESET_FLAG
// 2 STEP (W=1 steps, and returns to halted)
// 1 HALT(ED)
// 0 HALT
// DBGDATA
// read/writes internal registers
//
localparam HALT_BIT = 0,
STEP_BIT = 2,
RESET_BIT = 3,
CLEAR_CACHE_BIT = 4,
CATCH_BIT = 5;
// }}}
wire cpu_clken;
wire dbg_cyc, dbg_stb, dbg_we, dbg_stall;
wire [5:0] dbg_addr;
wire [DBG_WIDTH-1:0] dbg_idata;
wire [DBG_WIDTH/8-1:0] dbg_sel;
reg [DBG_WIDTH-1:0] dbg_odata;
reg dbg_ack;
wire cpu_break, dbg_cmd_write,
dbg_cpu_write, dbg_cpu_read;
wire reset_hold, halt_on_fault, dbg_catch;
wire reset_request, release_request, halt_request,
step_request, clear_cache_request;
reg cmd_reset, cmd_halt, cmd_step, cmd_clear_cache,
cmd_write, cmd_read;
reg [2:0] cmd_read_ack;
reg [4:0] cmd_waddr;
reg [DBG_WIDTH-1:0] cmd_wdata;
wire [2:0] cpu_dbg_cc;
wire cpu_reset, cpu_halt, cpu_dbg_stall,
cpu_has_halted;
wire cpu_lcl_cyc, cpu_lcl_stb,
cpu_op_stall, cpu_pf_stall, cpu_i_count;
wire [DBG_WIDTH-1:0] cpu_dbg_data;
wire [DBG_WIDTH-1:0] cpu_status;
wire [DBG_WIDTH-1:0] dbg_cmd_data;
wire [DBG_WIDTH/8-1:0] dbg_cmd_strb;
reg dbg_pre_ack;
reg [DBG_WIDTH-1:0] dbg_cpu_status;
// }}}
////////////////////////////////////////////////////////////////////////
//
// Debug bus signal renaming
// {{{
////////////////////////////////////////////////////////////////////////
//
//
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 dbg_sel = i_dbg_sel;
assign o_dbg_ack = dbg_ack;
assign o_dbg_stall = dbg_stall;
assign o_dbg_data = dbg_odata;
// }}}
////////////////////////////////////////////////////////////////////////
//
// The external debug interface
// {{{
////////////////////////////////////////////////////////////////////////
//
//
assign dbg_cpu_write = OPT_DBGPORT && (dbg_stb && !dbg_stall && dbg_we)
&& (dbg_addr[5] == DBG_ADDR_CPU)
&& dbg_sel == 4'hf;
assign dbg_cpu_read = (dbg_stb && !dbg_stall && !dbg_we
&& dbg_addr[5] == DBG_ADDR_CPU);
assign dbg_cmd_write = (dbg_stb && !dbg_stall && dbg_we)
&&(dbg_addr[5] == DBG_ADDR_CTRL);
assign dbg_cmd_data = dbg_idata;
assign dbg_cmd_strb = dbg_sel;
assign reset_request = dbg_cmd_write && dbg_cmd_strb[RESET_BIT/8]
&& dbg_cmd_data[RESET_BIT];
assign release_request = dbg_cmd_write && dbg_cmd_strb[HALT_BIT/8]
&& !dbg_cmd_data[HALT_BIT];
assign halt_request = dbg_cmd_write && dbg_cmd_strb[HALT_BIT/8]
&& dbg_cmd_data[HALT_BIT];
assign step_request = dbg_cmd_write && dbg_cmd_strb[STEP_BIT/8]
&& dbg_cmd_data[STEP_BIT];
assign clear_cache_request = dbg_cmd_write
&& dbg_cmd_strb[CLEAR_CACHE_BIT/8]
&& dbg_cmd_data[CLEAR_CACHE_BIT];
//
// reset_hold: Always start us off with an initial reset
// {{{
generate if (RESET_DURATION > 0)
begin : INITIAL_RESET_HOLD
// {{{
reg [$clog2(RESET_DURATION)-1:0] reset_counter;
reg r_reset_hold;
initial reset_counter = RESET_DURATION;
always @(posedge i_clk)
if (i_reset)
reset_counter <= RESET_DURATION;
else if (reset_counter > 0)
reset_counter <= reset_counter - 1;
initial r_reset_hold = 1;
always @(posedge i_clk)
if (i_reset)
r_reset_hold <= 1;
else
r_reset_hold <= (reset_counter > 1);
assign reset_hold = r_reset_hold;
`ifdef FORMAL
always @(*)
assert(reset_hold == (reset_counter != 0));
`endif
// }}}
end else begin : NO_RESET_HOLD
assign reset_hold = 0;
end endgenerate
// }}}
assign halt_on_fault = dbg_catch;
// cmd_reset
// {{{
// Always start us off with an initial reset
initial cmd_reset = 1'b1;
always @(posedge i_clk)
if (i_reset)
cmd_reset <= 1'b1;
else if (reset_hold)
cmd_reset <= 1'b1;
else if (cpu_break && !halt_on_fault)
cmd_reset <= 1'b1;
else
cmd_reset <= reset_request;
// }}}
// cmd_halt
// {{{
initial cmd_halt = START_HALTED;
always @(posedge i_clk)
if (i_reset)
cmd_halt <= START_HALTED;
else if (cmd_reset && START_HALTED)
cmd_halt <= START_HALTED;
else begin
// {{{
// When shall we release from a halt? Only if we have
// come to a full and complete stop. Even then, we only
// release if we aren't being given a command to step the CPU.
//
if (!cmd_write && cpu_has_halted && dbg_cmd_write
&& (release_request || step_request))
cmd_halt <= 1'b0;
// Reasons to halt
// 1. Halt on any unhandled CPU exception. The cause of the
// exception must be cured before we can (re)start.
// If the CPU is configured to start immediately on power
// up, we leave it to reset on any exception instead.
if (cpu_break && halt_on_fault)
cmd_halt <= 1'b1;
// 2. Halt on any user request to halt. (Only valid if the
// STEP bit isn't also set)
if (dbg_cmd_write && halt_request && !step_request)
cmd_halt <= 1'b1;
// 3. Halt on any user request to write to a CPU register
if (dbg_cpu_write)
cmd_halt <= 1'b1;
// 4. Halt following any step command
if (cmd_step && !step_request)
cmd_halt <= 1'b1;
// 5. Halt following any clear cache
if (cmd_clear_cache)
cmd_halt <= 1'b1;
// 6. Halt on any clear cache bit--independent of any step bit
if (clear_cache_request)
cmd_halt <= 1'b1;
// }}}
end
// }}}
// cmd_clear_cache
// {{{
initial cmd_clear_cache = 1'b0;
always @(posedge i_clk)
if (i_reset || cpu_reset)
cmd_clear_cache <= 1'b0;
else if (dbg_cmd_write && clear_cache_request && halt_request)
cmd_clear_cache <= 1'b1;
else if (cmd_halt && !cpu_dbg_stall)
cmd_clear_cache <= 1'b0;
// }}}
// cmd_step
// {{{
initial cmd_step = 1'b0;
always @(posedge i_clk)
if (i_reset)
cmd_step <= 1'b0;
else if (cmd_reset || cpu_break
|| reset_request
|| clear_cache_request || cmd_clear_cache
|| halt_request || dbg_cpu_write)
cmd_step <= 1'b0;
else if (!cmd_write && cpu_has_halted && step_request)
cmd_step <= 1'b1;
else // if (cpu_dbg_stall)
cmd_step <= 1'b0;
`ifdef FORMAL
// While STEP is true, we can't halt
always @(*)
if (!i_reset && cmd_step)
assert(!cmd_halt);
`endif
// }}}
// dbg_catch
// {{{
generate if (!OPT_DBGPORT)
begin : NO_DBG_CATCH
assign dbg_catch = START_HALTED;
end else begin : GEN_DBG_CATCH
reg r_dbg_catch;
initial r_dbg_catch = START_HALTED;
always @(posedge i_clk)
if (i_reset)
r_dbg_catch <= START_HALTED;
else if (dbg_cmd_write && dbg_cmd_strb[CATCH_BIT/8])
r_dbg_catch <= dbg_cmd_data[CATCH_BIT];
assign dbg_catch = r_dbg_catch;
end endgenerate
// }}}
assign cpu_reset = (cmd_reset);
assign cpu_halt = (cmd_halt);
// cpu_status
// {{{
// 0xffff_f000 -> (Unused / reserved)
//
// 0x0000_0800 -> cpu_break
// 0x0000_0400 -> Interrupt pending
// 0x0000_0200 -> User mode
// 0x0000_0100 -> Sleep (CPU is sleeping)
//
// 0x0000_00c0 -> (Unused/reserved)
// 0x0000_0020 -> dbg_catch
// 0x0000_0010 -> cmd_clear_cache
//
// 0x0000_0008 -> Reset
// 0x0000_0004 -> Step (auto clearing, write only)
// 0x0000_0002 -> Halt (status)
// 0x0000_0001 -> Halt (request)
assign cpu_status = { 16'h0, 4'h0,
cpu_break, i_ext_int, cpu_dbg_cc[1:0],
2'h0, dbg_catch, 1'b0,
cmd_reset, 1'b0, !cpu_dbg_stall, cmd_halt
};
// }}}
// cmd_write
// {{{
initial cmd_write = 0;
always @(posedge i_clk)
if (i_reset || cpu_reset)
cmd_write <= 1'b0;
else if (!cmd_write || cpu_has_halted)
cmd_write <= dbg_cpu_write;
// }}}
// cmd_read
// {{{
initial cmd_read = 0;
always @(posedge i_clk)
if (i_reset || !dbg_cyc || !OPT_DBGPORT)
cmd_read <= 1'b0;
else if (dbg_cpu_read)
cmd_read <= 1'b1;
else if (cmd_read_ack == 1)
cmd_read <= 1'b0;
initial cmd_read_ack = 0;
always @(posedge i_clk)
if (i_reset || !dbg_cyc || !OPT_DBGPORT)
cmd_read_ack <= 0;
else if (dbg_cpu_read)
cmd_read_ack <= 2 + (OPT_DISTRIBUTED_REGS ? 0:1);
else if (cmd_read_ack > 0)
cmd_read_ack <= cmd_read_ack - 1;
// }}}
// cmd_waddr, cmd_wdata
// {{{
always @(posedge i_clk)
if ((!cmd_write || cpu_has_halted) && dbg_cpu_write)
begin
cmd_waddr <= dbg_addr[4:0];
cmd_wdata <= dbg_idata;
end
// }}}
// }}}
////////////////////////////////////////////////////////////////////////
//
// The CPU itself
// {{{
////////////////////////////////////////////////////////////////////////
//
//
assign cpu_clken = cmd_write || cmd_read || dbg_cyc;
`ifdef FORMAL
// {{{
(* anyseq *) reg f_cpu_halted, f_cpu_data, f_cpu_stall,
f_cpu_break;
(* anyseq *) reg [2:0] f_cpu_dbg_cc;
(* anyseq *) reg [31:0] f_cpu_dbg_data;
assign cpu_dbg_stall = f_cpu_stall && !f_cpu_halted;
assign cpu_break = f_cpu_break;
assign cpu_dbg_cc = f_cpu_dbg_cc;
assign cpu_dbg_data = f_cpu_dbg_data;
assign cpu_has_halted= f_cpu_halted;
fdebug #(
// {{{
.OPT_START_HALTED(START_HALTED),
.OPT_DISTRIBUTED_RAM(OPT_DISTRIBUTED_REGS)
// }}}
) fdbg (
// {{{
.i_clk(i_clk),
.i_reset(i_reset),
.i_cpu_reset(cpu_reset),
.i_halt(cpu_halt),
.i_halted(f_cpu_halted),
.i_clear_cache(cmd_clear_cache),
.i_dbg_we(cmd_write),
.i_dbg_reg(cmd_waddr),
.i_dbg_data(cmd_wdata),
.i_dbg_stall(cpu_dbg_stall),
.i_dbg_break(cpu_break),
.i_dbg_cc(cpu_dbg_cc)
// }}}
);
// }}}
`else
zipwb #(
// {{{
.RESET_ADDRESS(RESET_ADDRESS),
.ADDRESS_WIDTH(ADDRESS_WIDTH-$clog2(BUS_WIDTH/8)),
.BUS_WIDTH(BUS_WIDTH),
.OPT_PIPELINED(OPT_PIPELINED),
.OPT_EARLY_BRANCHING(OPT_EARLY_BRANCHING),
.OPT_LGICACHE(OPT_LGICACHE),
.OPT_LGDCACHE(OPT_LGDCACHE),
.OPT_MPY(OPT_MPY),
.OPT_DIV(OPT_DIV),
.OPT_SHIFTS(OPT_SHIFTS),
.IMPLEMENT_FPU(OPT_FPU),
.OPT_CIS(OPT_CIS),
.OPT_LOCK(OPT_LOCK),
.OPT_LOWPOWER(OPT_LOWPOWER),
.OPT_START_HALTED(START_HALTED),
.OPT_SIM(OPT_SIM),
.OPT_DBGPORT(OPT_DBGPORT),
.OPT_TRACE_PORT(OPT_TRACE_PORT),
.OPT_PROFILER(OPT_PROFILER),
.OPT_CLKGATE(OPT_CLKGATE),
.OPT_DISTRIBUTED_REGS(OPT_DISTRIBUTED_REGS),
.OPT_USERMODE(OPT_USERMODE),
.WITH_LOCAL_BUS(0)
// }}}
) thecpu(
// {{{
.i_clk(i_clk), .i_reset(cpu_reset), .i_interrupt(i_ext_int),
.i_cpu_clken(cpu_clken),
// Debug interface
// {{{
.i_halt(cpu_halt), .i_clear_cache(cmd_clear_cache),
.i_dbg_wreg(cmd_waddr), .i_dbg_we(cmd_write),
.i_dbg_data(cmd_wdata),
.i_dbg_rreg(dbg_addr[4:0]),
.o_dbg_stall(cpu_dbg_stall),
.o_halted(cpu_has_halted),
.o_dbg_reg(cpu_dbg_data),
.o_dbg_cc(cpu_dbg_cc),
.o_break(cpu_break),
// }}}
// Wishbone bus interface
// {{{
.o_wb_gbl_cyc(o_wb_cyc), .o_wb_gbl_stb(o_wb_stb),
.o_wb_lcl_cyc(cpu_lcl_cyc),
.o_wb_lcl_stb(cpu_lcl_stb),
.o_wb_we(o_wb_we), .o_wb_addr(o_wb_addr),
.o_wb_data(o_wb_data), .o_wb_sel(o_wb_sel),
// Return values from the Wishbone bus
.i_wb_stall(i_wb_stall), .i_wb_ack(i_wb_ack),
.i_wb_data(i_wb_data),
.i_wb_err((i_wb_err)||(cpu_lcl_cyc)),
// }}}
.o_op_stall(cpu_op_stall), .o_pf_stall(cpu_pf_stall),
.o_i_count(cpu_i_count),
.o_debug(o_cpu_debug),
//
.o_prof_stb(o_prof_stb),
.o_prof_addr(o_prof_addr),
.o_prof_ticks(o_prof_ticks)
// }}}
);
`endif
// }}}
////////////////////////////////////////////////////////////////////////
//
// Return debug response values
// {{{
////////////////////////////////////////////////////////////////////////
//
//
// always @(posedge i_clk)
// dbg_pre_addr <= dbg_addr[5];
always @(posedge i_clk)
dbg_cpu_status <= cpu_status;
initial dbg_pre_ack = 1'b0;
always @(posedge i_clk)
if (i_reset || !i_dbg_cyc)
dbg_pre_ack <= 1'b0;
else
dbg_pre_ack <= dbg_stb && !dbg_stall && !dbg_cpu_read;
initial dbg_ack = 1'b0;
always @(posedge i_clk)
if (i_reset || !i_dbg_cyc)
dbg_ack <= 1'b0;
else
dbg_ack <= dbg_pre_ack || (cmd_read_ack == 1);
always @(posedge i_clk)
if (!OPT_LOWPOWER || dbg_pre_ack || cmd_read)
begin
if (cmd_read)
dbg_odata <= cpu_dbg_data;
else
dbg_odata <= dbg_cpu_status;
end
assign dbg_stall = cmd_read || (cmd_write && cpu_dbg_stall
&& dbg_addr[5] == DBG_ADDR_CPU);
// }}}
assign o_ext_int = (cmd_halt) && (!i_wb_stall);
// Make Verilator happy
// {{{
// verilator lint_off UNUSED
wire unused;
assign unused = &{ 1'b0, dbg_cyc, cpu_lcl_stb, cpu_op_stall,
cpu_dbg_cc[2], cpu_pf_stall, cpu_i_count };
// verilator lint_on UNUSED
// }}}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//
// Formal properties
// {{{
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
`ifdef FORMAL
localparam F_LGDEPTH = 3;
reg [F_LGDEPTH-1:0] fwb_nreqs, fwb_nacks, fwb_outstanding;
wire cpu_dbg_we;
assign cpu_dbg_we = (dbg_stb && !dbg_stall && dbg_we
&&(dbg_addr[5] == DBG_ADDR_CPU));
fwb_slave #(
.AW(6), .DW(DBG_WIDTH), .F_LGDEPTH(F_LGDEPTH)
) fwb(
// {{{
.i_clk(i_clk), .i_reset(i_reset),
.i_wb_cyc(i_dbg_cyc), .i_wb_stb(i_dbg_stb && !o_dbg_stall),
.i_wb_we(i_dbg_we), .i_wb_addr(i_dbg_addr),
.i_wb_data(i_dbg_data),
.i_wb_ack(o_dbg_ack), .i_wb_stall(o_dbg_stall),
.i_wb_idata(o_dbg_data), .i_wb_err(1'b0),
.f_nreqs(fwb_nreqs), .f_nacks(fwb_nacks),
.f_outstanding(fwb_outstanding)
// }}}
);
always @(*)
if (i_dbg_cyc)
begin
if (cmd_read_ack > 0)
begin
assert(!dbg_pre_ack);
assert(fwb_outstanding == 1 + (o_dbg_ack ? 1:0));
end else
assert(fwb_outstanding == dbg_pre_ack + o_dbg_ack);
end
always @(posedge i_clk)
if ($past(i_dbg_cyc && cpu_dbg_we))
assume(i_dbg_cyc);
`endif
// }}}
endmodule