URL
https://opencores.org/ocsvn/s6soc/s6soc/trunk
Subversion Repositories s6soc
Compare Revisions
- This comparison shows the changes necessary to convert path
/s6soc/trunk/rtl
- from Rev 13 to Rev 16
- ↔ Reverse comparison
Rev 13 → Rev 16
/cpu/ziptimer.v
120,7 → 120,9
// Set the interrupt on our last tick. |
initial o_int = 1'b0; |
always @(posedge i_clk) |
if (i_ce) |
if (i_rst) |
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; |
/cpu/cpudefs.v
266,6 → 266,6
// `define INCLUDE_ACCOUNTING_COUNTERS |
// |
// |
// `define DEBUG_SCOPE |
`define DEBUG_SCOPE |
// |
`endif // CPUDEFS_H |
/cpu/cpuops.v
240,8 → 240,8
// +(al*bl) |
// - 2^31 (2^16 bh+bl + 2^16 ah+al + 2^31) |
// |
reg [31:0] pp_f, pp_l; // pp_o, pp_i, pp_l; |
reg [32:0] pp_oi; |
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 |
/cpu/zipcpu.v
304,7 → 304,8
// |
// |
reg [(AW-1):0] alu_pc; |
reg alu_pc_valid, mem_pc_valid; |
reg r_alu_pc_valid, mem_pc_valid; |
wire alu_pc_valid; |
wire alu_phase; |
wire alu_ce, alu_stall; |
wire [31:0] alu_result; |
375,11 → 376,8
// |
// PIPELINE STAGE #2 :: Instruction Decode |
// Calculate stall conditions |
`ifdef OPT_PIPELINED |
assign dcd_ce = ((~dcdvalid)||(~dcd_stalled))&&(~clear_pipeline); |
`else |
assign dcd_ce = 1'b1; |
`endif |
|
`ifdef OPT_PIPELINED |
assign dcd_stalled = (dcdvalid)&&(op_stall); |
`else |
428,7 → 426,7
assign op_ce = ((dcdvalid)||(dcd_illegal))&&(~op_stall)&&(~clear_pipeline); |
`else |
assign op_stall = (opvalid)&&(~master_ce); |
assign op_ce = ((dcdvalid)||(dcd_illegal)); |
assign op_ce = ((dcdvalid)||(dcd_illegal))&&(~clear_pipeline); |
`endif |
|
// |
458,7 → 456,7
`else |
assign alu_stall = ((~master_ce)&&(opvalid_alu)) |
||((opvalid_alu)&&(op_break)); |
assign alu_ce = (master_ce)&&((opvalid_alu)||(op_illegal))&&(~alu_stall); |
assign alu_ce = (master_ce)&&((opvalid_alu)||(op_illegal))&&(~alu_stall)&&(~clear_pipeline); |
`endif |
// |
|
473,7 → 471,7
// If we aren't pipelined, then no one will be changing what's in the |
// pipeline (i.e. clear_pipeline), while our only instruction goes |
// through the ... pipeline. |
assign mem_ce = (master_ce)&&(opvalid_mem)&&(~mem_stalled); |
assign mem_ce = (master_ce)&&(opvalid_mem)&&(~mem_stalled)&&(~clear_pipeline); |
`endif |
`ifdef OPT_PIPELINED_BUS_ACCESS |
assign mem_stalled = (~master_ce)||(alu_busy)||((opvalid_mem)&&( |
512,7 → 510,7
|
assign pf_ce = (~pf_valid)&&(~dcdvalid)&&(~opvalid)&&(~alu_valid); |
prefetch #(ADDRESS_WIDTH) |
pf(i_clk, i_rst, (pf_ce), (~dcd_stalled), pf_pc, gie, |
pf(i_clk, (i_rst), (pf_ce), (~dcd_stalled), pf_pc, gie, |
instruction, instruction_pc, instruction_gie, |
pf_valid, pf_illegal, |
pf_cyc, pf_stb, pf_we, pf_addr, pf_data, |
520,7 → 518,7
|
initial r_dcdvalid = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
if ((i_rst)||(clear_pipeline)) |
r_dcdvalid <= 1'b0; |
else if (dcd_ce) |
r_dcdvalid <= (pf_valid); |
565,7 → 563,7
if ((i_rst)||(clear_pipeline)) |
r_dcdvalid <= 1'b0; |
else if (dcd_ce) |
r_dcdvalid <= (pf_valid)&&(~clear_pipeline)&&(~dcd_ljmp)&&((~r_dcdvalid)||(~dcd_early_branch)); |
r_dcdvalid <= (pf_valid)&&(~dcd_ljmp)&&((~r_dcdvalid)||(~dcd_early_branch)); |
else if (op_ce) |
r_dcdvalid <= 1'b0; |
assign dcdvalid = r_dcdvalid; |
1160,13 → 1158,16
assign alu_illegal = (alu_illegal_op)||(r_alu_illegal); |
`endif |
|
initial alu_pc_valid = 1'b0; |
initial r_alu_pc_valid = 1'b0; |
initial mem_pc_valid = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
alu_pc_valid <= 1'b0; |
else |
alu_pc_valid <= (alu_ce); |
r_alu_pc_valid <= 1'b0; |
else if (alu_ce) // Includes && (~alu_clear_pipeline) |
r_alu_pc_valid <= 1'b1; |
else if ((~alu_busy)||(clear_pipeline)) |
r_alu_pc_valid <= 1'b0; |
assign alu_pc_valid = (r_alu_pc_valid)&&(~alu_busy); |
always @(posedge i_clk) |
if (i_rst) |
mem_pc_valid <= 1'b0; |
1638,7 → 1639,9
else if ((new_pc)||((~dcd_stalled)&&(pf_valid))) |
pf_pc <= pf_pc + {{(AW-1){1'b0}},1'b1}; |
`else |
else if (((alu_pc_valid)&&(~clear_pipeline))||(mem_pc_valid)) |
else if ((alu_gie==gie)&&( |
((alu_pc_valid)&&(~clear_pipeline)) |
||(mem_pc_valid))) |
pf_pc <= alu_pc; |
`endif |
|
1707,20 → 1710,45
`ifdef DEBUG_SCOPE |
always @(posedge i_clk) |
o_debug <= { |
/* |
o_break, i_wb_err, pf_pc[1:0], |
// |
flags, |
pf_valid, dcdvalid, opvalid, alu_valid, mem_valid, |
// |
pf_valid, dcdvalid, opvalid, alu_valid, |
// |
mem_valid, |
op_ce, alu_ce, mem_ce, |
// |
master_ce, opvalid_alu, opvalid_mem, |
master_ce, |
opvalid_alu, opvalid_mem, alu_stall, |
// |
alu_stall, mem_busy, op_pipe, mem_pipe_stalled, |
mem_busy, op_pipe, |
`ifdef OPT_PIPELINED_BUS_ACCESS |
mem_pipe_stalled, |
`else |
1'b0, |
`endif |
mem_we, |
// |
// ((opvalid_alu)&&(alu_stall)) |
// ||((opvalid_mem)&&(~op_pipe)&&(mem_busy)) |
// ||((opvalid_mem)&&( op_pipe)&&(mem_pipe_stalled))); |
// opA[23:20], opA[3:0], |
gie, sleep, wr_reg_ce, wr_reg_vl[4:0] |
*/ |
|
o_break, i_wb_err, o_wb_gbl_cyc, o_wb_gbl_stb, |
pf_valid, dcdvalid, opvalid, alu_valid, |
mem_valid, dcd_ce, op_ce, alu_ce, |
mem_ce, |
dcd_illegal, gie, sleep, |
{ ((o_wb_gbl_cyc)&&(o_wb_gbl_stb)&&(o_wb_we)) |
? o_wb_data[15:0] |
: ((o_wb_gbl_cyc)&&(o_wb_gbl_stb)&&(~o_wb_we)&&(i_wb_ack)) |
? i_wb_data[15:0] |
: o_wb_addr[15:0] |
} |
/* |
i_rst, master_ce, (new_pc), |
((dcd_early_branch)&&(dcdvalid)), |
/builddate.v
1,20 → 1710,45
`define DATESTAMP 32'h20160430 |
`define DATESTAMP 32'h20160505 |
/wbscopc.v
0,0 → 1,220
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbscopc.v |
// |
// Project: FPGA Library of Routines |
// |
// Purpose: This scope is identical in function to the wishbone scope |
// found in wbscope, save that the output is compressed and that (as a |
// result) it can only handle recording 31 bits at a time. This allows |
// the top bit to indicate an 'address difference'. Okay, there's |
// another difference as well: this version only works in a synchronous |
// fashion with the clock from the WB bus. You cannot have a separate |
// bus and data clock. |
// |
// Reading/decompressing the output of this scope works in this fashion: |
// Once the scope has stopped, read from the port. Any time the high |
// order bit is set, the other 31 bits tell you how many times to repeat |
// the last value. If the high order bit is not set, then the value |
// is a new data value. |
// |
// I've provided this version of a compressed scope to OpenCores for |
// discussion purposes. While wbscope.v works and works well by itself, |
// this compressed scope has a couple of fundamental flaw that I have |
// yet to fix. One of them is that it is impossible to know when the |
// trigger took place. The second problem is that it may be impossible |
// to know the state of the scope at the beginning of the buffer--should |
// the buffer begin with an address difference value instead of a data |
// value. |
// |
// Ideally, the first item read out of the scope should be a data value, |
// even if the scope was skipping values to a new address at the time. |
// If it was in the middle of a skip, the next item out of the scope |
// should be the skip length. This, though, violates the rule that there |
// are (1<<LGMEMLEN) items in the memory, and that the trigger took place |
// on the last item of memory ... so that portion of this compressed |
// scope is still to be defined. |
// |
// Like I said, this version is placed here for discussion purposes, |
// not because it runs well nor because I have recognized that it has any |
// particular value (yet). |
// |
// Well, I take that back. When dealing with an interface such as the |
// PS/2 interface, or even the 16x2 LCD interface, it is often true |
// that things change _very_ slowly. They could change so slowly that |
// the other approach to the scope doesn't work. This then gives you |
// a working scope, by only capturing the changes. You'll still need |
// to figure out (after the fact) when the trigge took place. Perhaps |
// you'll wish to add the trigger as another data line, so you can find |
// when it took place in your own data? |
// |
// Okay, I take that back twice: I'm finding this compressed scope very |
// valuable for evaluating the timing associated with a GPS PPS and |
// associated NMEA stream. I need to collect over a seconds worth of |
// data, and I don't have enough memory to handle one memory value per |
// clock, yet I still want to know exactly when the GPS PPS goes high, |
// when it goes low, when I'm adjusting my clock, and when the clock's |
// PPS output goes high. Did I synchronize them well? Oh, and when does |
// the NMEA time string show up when compared with the PPS? All of those |
// are valuable, but could never be done if the scope wasn't compressed. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015, 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 |
// |
// |
///////////////////////////////////////////////////////////////////////////// |
// |
// |
module wbscopc(i_clk, i_ce, i_trigger, i_data, |
i_wb_clk, i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, |
o_wb_ack, o_wb_stall, o_wb_data, |
o_interrupt); |
parameter LGMEM = 5'd10, NELM=31, BUSW = 32, SYNCHRONOUS=1; |
// The input signals that we wish to record |
input i_clk, i_ce, i_trigger; |
input [(NELM-1):0] i_data; |
// The WISHBONE bus for reading and configuring this scope |
input i_wb_clk, i_wb_cyc, i_wb_stb, i_wb_we; |
input i_wb_addr; // One address line only |
input [(BUSW-1):0] i_wb_data; |
output wire o_wb_ack, o_wb_stall; |
output wire [(BUSW-1):0] o_wb_data; |
// And, finally, for a final flair --- offer to interrupt the CPU after |
// our trigger has gone off. This line is equivalent to the scope |
// being stopped. It is not maskable here. |
output wire o_interrupt; |
|
|
// Let's first see how far we can get by cheating. We'll use the |
// wbscope program, and suffer a lack of several features |
|
// When is the full scope reset? Capture that reset bit from any |
// write. |
wire lcl_reset; |
assign lcl_reset = (i_wb_cyc)&&(i_wb_stb)&&(~i_wb_addr)&&(i_wb_we) |
&&(~i_wb_data[31]); |
|
// A big part of this scope is the 'address' of any particular |
// data value. As of this current version, the 'address' changed |
// in definition from an absolute time (which had all kinds of |
// problems) to a difference in time. Hence, when the address line |
// is high on decompression, the 'address' field will record an |
// address difference. |
// |
// To implement this, we set our 'address' to zero any time the |
// data changes, but increment it on all other clocks. Should the |
// address difference get to our maximum value, we let it saturate |
// rather than overflow. |
reg [(BUSW-2):0] ck_addr; |
reg [(NELM-1):0] lst_dat; |
initial ck_addr = 0; |
always @(posedge i_clk) |
if ((lcl_reset)||((i_ce)&&(i_data != lst_dat))) |
ck_addr <= 0; |
else if (&ck_addr) |
; // Saturated (non-overflowing) address diff |
else |
ck_addr <= ck_addr + 1; |
|
wire [(BUSW-2):0] w_data; |
generate |
if (NELM == BUSW-1) |
assign w_data = i_data; |
else |
assign w_data = { {(BUSW-NELM-1){1'b0}}, i_data }; |
endgenerate |
|
// |
// To do our compression, we keep track of two registers: the most |
// recent data to the device (imm_ prefix) and the data from one |
// clock ago. This allows us to suppress writes to the scope which |
// would otherwise be two address writes in a row. |
reg imm_adr, lst_adr; // Is this an address (1'b1) or data value? |
reg [(BUSW-2):0] lst_val, // Data for the scope, delayed by one |
imm_val; // Data to write to the scope |
initial lst_dat = 0; |
initial lst_adr = 1'b1; |
initial imm_adr = 1'b1; |
always @(posedge i_clk) |
if (lcl_reset) |
begin |
imm_val <= 31'h0; |
imm_adr <= 1'b1; |
lst_val <= 31'h0; |
lst_adr <= 1'b1; |
lst_dat <= 0; |
end else if ((i_ce)&&(i_data != lst_dat)) |
begin |
imm_val <= w_data; |
imm_adr <= 1'b0; |
lst_val <= imm_val; |
lst_adr <= imm_adr; |
lst_dat <= i_data; |
end else begin |
imm_val <= ck_addr; // Minimum value here is '1' |
imm_adr <= 1'b1; |
lst_val <= imm_val; |
lst_adr <= imm_adr; |
end |
|
// |
// Here's where we suppress writing pairs of address words to the |
// scope at once. |
// |
reg r_ce; |
reg [(BUSW-1):0] r_data; |
initial r_ce = 1'b0; |
always @(posedge i_clk) |
r_ce <= (~lst_adr)||(~imm_adr); |
always @(posedge i_clk) |
r_data <= ((~lst_adr)||(~imm_adr)) |
? { lst_adr, lst_val } |
: { {(32 - NELM){1'b0}}, i_data }; |
|
|
// |
// The trigger needs some extra attention, in order to keep triggers |
// that happen between events from being ignored. |
// |
wire w_trigger; |
assign w_trigger = (r_trigger)||(i_trigger); |
|
reg r_trigger; |
initial r_trigger = 1'b0; |
always @(posedge i_clk) |
if (lcl_reset) |
r_trigger <= 1'b0; |
else |
r_trigger <= w_trigger; |
|
// |
// Call the regular wishbone scope to do all of our real work, now |
// that we've compressed the input. |
// |
wbscope #(.SYNCHRONOUS(1), .LGMEM(LGMEM), |
.BUSW(BUSW)) cheatersscope(i_clk, r_ce, w_trigger, r_data, |
i_wb_clk, i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, |
o_wb_ack, o_wb_stall, o_wb_data, o_interrupt); |
endmodule |
/busmaster.v
39,12 → 39,15
`include "builddate.v" |
// |
`define INCLUDE_ZIPPY |
`define IMPLEMENT_ONCHIP_RAM // 2804 w/o after synthesis |
`define IMPLEMENT_ONCHIP_RAM |
`ifndef VERILATOR |
`define FANCY_ICAP_ACCESS |
`endif |
`define FLASH_ACCESS |
`define DBG_SCOPE // About 204 LUTs, at 2^6 addresses |
// `define COMPRESSED_SCOPE |
`define INCLUDE_SECOND_TIMER |
`define INCLUDE_CPU_RESET_LOGIC |
// `define INCLUDE_RTC // About 90 LUTs |
module busmaster(i_clk, i_rst, |
i_rx_stb, i_rx_data, o_tx_stb, o_tx_data, i_tx_busy, |
122,7 → 125,7
wire zip_cyc, zip_stb, zip_we, zip_cpu_int; |
wire [(ZA-1):0] w_zip_addr; |
wire [(BAW-1):0] zip_addr; |
wire [31:0] zip_data; |
wire [31:0] zip_data, zip_scope_data; |
// and then coming from devices |
wire zip_ack, zip_stall, zip_err; |
wire dwb_we, dwb_stb, dwb_cyc, dwb_ack, dwb_stall, dwb_err; |
144,15 → 147,33
// in the flash without needing to change our FPGA load and vice versa. |
// |
// 23'h404000 |
wire cpu_reset; |
`ifdef INCLUDE_CPU_RESET_LOGIC |
reg btn_reset, x_button, r_button; |
initial btn_reset = 1'b0; |
initial x_button = 1'b0; |
initial r_button = 1'b0; |
always @(posedge i_clk) |
begin |
x_button <= i_btn[1]; |
r_button <= x_button; |
btn_reset <= ((r_button)&&(zip_cpu_int))||(tmrb_int); |
end |
assign cpu_reset = btn_reset; |
`else |
assign cpu_reset = 1'b0; |
`endif |
|
zipbones #(CMOD_ZIPCPU_RESET_ADDRESS,ZA,6) |
thecpu(i_clk, 1'b0, |
thecpu(i_clk, btn_reset, // 1'b0, |
// Zippys wishbone interface |
wb_cyc, wb_stb, wb_we, w_zip_addr, wb_data, |
wb_ack, wb_stall, wb_idata, wb_err, |
w_interrupt, zip_cpu_int, |
// Debug wishbone interface |
// Debug wishbone interface -- not really used |
1'b0, 1'b0,1'b0, 1'b0, 32'h00, |
zip_dbg_ack, zip_dbg_stall, zip_dbg_data); |
zip_dbg_ack, zip_dbg_stall, zip_dbg_data, |
zip_scope_data); |
generate |
if (ZA < BAW) |
assign wb_addr = { {(BAW-ZA){1'b0}}, w_zip_addr }; |
219,6 → 240,12
|
assign none_sel =((wb_cyc)&&(wb_stb)&&(io_addr==6'h0)); |
/* |
assign none_sel =((wb_cyc)&&(wb_stb)&& |
((io_addr==6'h0) |
||((~io_addr[5])&&(|wb_addr[22:14]))) |
); |
*/ |
/* |
assign many_sel =((wb_cyc)&&(wb_stb)&&( |
{3'h0, io_sel} |
+{3'h0, flctl_sel} |
270,14 → 297,24
wire zta_ack, zta_stall, ztb_ack, ztb_stall; |
ziptimer #(32,31) |
zipt_a(i_clk, 1'b0, 1'b1, wb_cyc, |
`ifdef INCLUDE_SECOND_TIMER |
(wb_stb)&&(io_sel)&&(wb_addr[3:0]==4'h2), |
`else |
(wb_stb)&&(io_sel)&&(wb_addr[3:1]==3'h1), |
`endif |
wb_we, wb_data, zta_ack, zta_stall, timer_a, |
tmra_int); |
`ifdef INCLUDE_SECOND_TIMER |
ziptimer #(32,31) |
zipt_b(i_clk, 1'b0, 1'b1, wb_cyc, |
zipt_b(i_clk, cpu_reset, 1'b1, wb_cyc, |
(wb_stb)&&(io_sel)&&(wb_addr[3:0]==4'h3), |
wb_we, wb_data, ztb_ack, ztb_stall, timer_b, |
tmrb_int); |
`else |
// assign timer_b = 32'h000; |
assign timer_b = timer_a; |
assign tmrb_int = 1'b0; |
`endif |
|
wire [31:0] rtc_data; |
`ifdef INCLUDE_RTC |
331,9 → 368,11
// |
// Special Purpose I/O: Keypad, button, LED status and control |
// |
wire [3:0] w_led; |
spio thespio(i_clk, wb_cyc,(wb_stb)&&(io_sel)&&(wb_addr[3:0]==4'h5),wb_we, |
wb_data, spio_data, o_kp_col, i_kp_row, i_btn, o_led, |
wb_data, spio_data, o_kp_col, i_kp_row, i_btn, w_led, |
keypad_int, button_int); |
assign o_led = { w_led[3]|w_interrupt,w_led[2]|zip_cpu_int,w_led[1:0] }; |
|
// |
// General purpose (sort of) I/O: (Bottom two bits robbed in each |
447,7 → 486,16
`ifdef DBG_SCOPE |
wire scop_cfg_trigger; |
assign scop_cfg_trigger = (wb_cyc)&&(wb_stb)&&(cfg_sel); |
wbscope #(5'ha) wbcfgscope(i_clk, 1'b1, scop_cfg_trigger, cfg_scope, |
// wire scop_trigger = scop_cfg_trigger; |
wire scop_trigger = (zip_cpu_int) || (cpu_reset); |
`ifdef COMPRESSED_SCOPE |
wbscopc #(5'ha) |
`else |
wbscope #(5'ha) |
`endif |
wbcfgscope(i_clk, 1'b1, scop_trigger, |
// cfg_scope, |
zip_scope_data[30:0], |
// Wishbone interface |
i_clk, wb_cyc, (wb_stb)&&(scop_sel), |
wb_we, wb_addr[0], wb_data, |