URL
https://opencores.org/ocsvn/openarty/openarty/trunk
Subversion Repositories openarty
Compare Revisions
- This comparison shows the changes necessary to convert path
/openarty/trunk
- from Rev 2 to Rev 3
- ↔ Reverse comparison
Rev 2 → Rev 3
/rtl/wboled.v
0,0 → 1,162
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wboled.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 wboled(i_clk, i_cyc, i_stb, i_we, i_addr, i_data, |
o_ack, o_stall, o_data, |
o_sck, o_cs_n, o_mosi, o_dbit, |
o_pwr, o_int); |
parameter CBITS=4; // 2^4*2@5ns -> 160ns/clock > 150ns min |
input i_clk, i_cyc, i_stb, i_we; |
input [1:0] i_addr; |
input [31:0] i_data; |
output reg o_ack; |
output wire o_stall; |
output reg [31:0] o_data; |
output wire o_sck, o_cs_n, o_mosi, o_dbit; |
output reg [2:0] o_pwr; |
output wire o_int; |
|
reg dev_wr, dev_dbit; |
reg [31:0] dev_word; |
reg [1:0] dev_len; |
wire dev_busy; |
lloled #(CBITS) |
lwlvl(i_clk, dev_wr, dev_dbit, dev_word, dev_len, dev_busy, |
o_sck, o_cs_n, o_mosi, o_dbit); |
|
reg r_busy; |
reg [3:0] r_len; |
reg [31:0] r_a, r_b; |
always @(posedge i_clk) |
if ((i_stb)&&(i_we)) |
begin |
if (i_addr[1:0]==2'b01) |
r_a <= i_data; |
if (i_addr[1:0]==2'b10) |
r_b <= i_data; |
end else if (r_cstb) |
begin |
r_a <= 32'h00; |
r_b <= 32'h00; |
end |
|
always @(posedge i_clk) |
begin |
case (i_addr) |
2'b00: o_data <= { 13'h00, o_pwr, 8'h00, r_len, 3'h0, r_busy }; |
2'b01: o_data <= r_a; |
2'b10: o_data <= r_b; |
2'b11: o_data <= { 13'h00, o_pwr, 8'h00, r_len, 3'h0, r_busy }; |
endcase |
end |
|
initial o_ack = 1'b0; |
always @(posedge i_clk) |
o_ack <= i_stb; |
assign o_stall = 1'b0; |
|
reg r_cstb, r_dstb, r_pstb; |
reg [23:0] r_data; |
initial r_cstb = 1'b0; |
initial r_dstb = 1'b0; |
initial r_pstb = 1'b0; |
always @(posedge i_clk) |
r_cstb <= (i_stb)&&(i_addr[1:0]==2'b00); |
always @(posedge i_clk) |
r_dstb <= (i_stb)&&(i_addr[1:0]==2'b11)&&(i_data[22:20]==3'h0); |
always @(posedge i_clk) |
r_pstb <= (i_stb)&&(i_addr[1:0]==2'b11)&&(i_data[22:20]!=3'h0); |
always @(posedge i_clk) |
r_data <= i_data[23:0]; |
|
initial o_pwr = 3'h0; |
always @(posedge i_clk) |
if (r_pstb) |
o_pwr <= ((o_pwr)&(~r_data[22:20])) |
|((i_data[18:16])&(r_data[22:20])); |
|
reg [3:0] b_len; |
always @(posedge i_clk) |
casez(i_data[31:28]) |
4'b000?: b_len <= (i_data[16])? 4'h1:4'h2; |
4'b0010: b_len <= 4'h3; |
4'b0011: b_len <= 4'h4; |
4'b0100: b_len <= 4'h5; |
4'b0101: b_len <= 4'h6; |
4'b0110: b_len <= 4'h7; |
4'b0111: b_len <= 4'h8; |
4'b1000: b_len <= 4'h9; |
4'b1001: b_len <= 4'ha; |
4'b1010: b_len <= 4'hb; |
default: b_len <= 4'h0; |
endcase |
|
reg [87:0] r_sreg; |
initial r_busy = 1'b0; |
always @(posedge i_clk) |
if ((~r_busy)&&(r_cstb)) |
begin |
dev_wr <= 1'b0; |
dev_dbit <= 1'b0; |
r_sreg <= { r_data[23:0], r_a, r_b }; |
r_len <= b_len; |
r_busy <= (b_len != 4'h0); |
if (b_len == 4'h1) |
r_sreg[87:72] <= { r_data[7:0], r_data[7:0] }; |
else if (b_len == 4'h2) |
r_sreg[87:72] <= r_data[15:0]; |
else |
r_sreg[87:72] <= r_data[23:8]; |
end else if ((~dev_busy)&&(r_dstb)) |
begin |
dev_wr <= 1'b0; |
dev_dbit <= 1'b1; |
r_sreg <= { r_data[15:0], 72'h00 }; |
r_len <= 4'h2; |
r_busy <= 1'b1; |
end else if ((r_busy)&&(~dev_busy)) |
begin |
dev_word <= r_sreg[87:56]; |
r_sreg <= { r_sreg[55:0], 32'h00 }; |
dev_len <= (r_len > 4'h4)? 2'b11:(r_len[1:0]+2'b11); |
r_len <= (r_len > 4'h4) ? (r_len-4'h4):0; |
end else if (r_busy) |
r_busy <= (r_len != 4'h0); |
|
assign o_int = (~r_busy); |
|
endmodule |
/rtl/memdev.v
0,0 → 1,73
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: memdev.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: This file is really simple: it creates an on-chip memory, |
// accessible via the wishbone bus, that can be used in this |
// project. The memory has single cycle pipeline access, although the |
// memory pipeline here still costs a cycle and there may be other cycles |
// lost between the ZipCPU (or whatever is the master of the bus) and this, |
// thus costing more cycles in access. Either way, operations can be |
// pipelined for single cycle access on subsequent transactions. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// |
module memdev(i_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); |
parameter AW=15, DW=32; |
input i_clk, i_wb_cyc, i_wb_stb, i_wb_we; |
input [(AW-1):0] i_wb_addr; |
input [(DW-1):0] i_wb_data; |
output reg o_wb_ack; |
output wire o_wb_stall; |
output reg [(DW-1):0] o_wb_data; |
|
reg last_wstb, last_stb; |
always @(posedge i_clk) |
last_wstb <= (i_wb_stb)&&(i_wb_we); |
always @(posedge i_clk) |
last_stb <= (i_wb_stb); |
|
reg [(DW-1):0] last_data; |
reg [(AW-1):0] last_addr; |
always @(posedge i_clk) |
last_data <= i_wb_data; |
always @(posedge i_clk) |
last_addr <= i_wb_addr; |
|
reg [(DW-1):0] mem [0:((1<<AW)-1)]; |
always @(posedge i_clk) |
o_wb_data <= mem[last_addr]; |
always @(posedge i_clk) |
if (last_wstb) |
mem[last_addr] <= last_data; |
always @(posedge i_clk) |
o_wb_ack <= (last_stb); |
assign o_wb_stall = 1'b0; |
|
endmodule |
/rtl/wbuinput.v
0,0 → 1,62
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbuinput.v |
// |
// Project: FPGA library |
// |
// Purpose: Coordinates the receiption of bytes, which are then translated |
// into codewords describing postential bus transactions. This |
// includes turning the human readable bytes into more compact bits, |
// forming those bits into codewords, and decompressing any that reference |
// addresses within a compression table. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
module wbuinput(i_clk, i_stb, i_byte, o_stb, o_codword); |
input i_clk, i_stb; |
input [7:0] i_byte; |
output wire o_stb; |
output wire [35:0] o_codword; |
|
wire hx_stb, hx_valid; |
wire [5:0] hx_hexbits; |
wbutohex tobits(i_clk, i_stb, i_byte, |
hx_stb, hx_valid, hx_hexbits); |
|
wire cw_stb; |
wire [35:0] cw_word; |
wbureadcw formcw(i_clk, hx_stb, hx_valid, hx_hexbits, |
cw_stb, cw_word); |
|
// `define DEBUGGING |
`ifdef DEBUGGING |
assign o_stb = cw_stb; |
assign o_codword = cw_word; |
`else |
wbudecompress unpack(i_clk,cw_stb,cw_word, o_stb, o_codword); |
`endif |
|
endmodule |
/rtl/wbusixchar.v
0,0 → 1,82
/////////////////////////////////////////////////////////////////////////// |
// |
// |
// Filename: wbusixchar.v |
// |
// Project: FPGA library |
// |
// Purpose: Supports a conversion from a six digit bus to a printable |
// ASCII character representing those six bits. The encoding is |
// as follows: |
// |
// 0-9 -> 0-9 |
// A-Z -> 10-35 |
// a-z -> 36-61 |
// @ -> 62 |
// % -> 63 |
// |
// Note that decoding is stateless, yet requires one clock. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// |
// |
module wbusixchar(i_clk, i_stb, i_bits, o_stb, o_char, o_busy, i_busy); |
input i_clk; |
input i_stb; |
input [6:0] i_bits; |
output reg o_stb; |
output reg [7:0] o_char; |
output wire o_busy; |
input i_busy; |
|
initial o_char = 8'h00; |
always @(posedge i_clk) |
if ((i_stb)&&(~o_busy)) |
begin |
if (i_bits[6]) |
o_char <= 8'h0a; |
else if (i_bits[5:0] <= 6'h09) // A digit, WORKS |
o_char <= "0" + { 4'h0, i_bits[3:0] }; |
else if (i_bits[5:0] <= 6'd35) // Upper case |
o_char <= "A" + { 2'h0, i_bits[5:0] } - 8'd10; // -'A'+10 |
else if (i_bits[5:0] <= 6'd61) |
o_char <= "a" + { 2'h0, i_bits[5:0] } - 8'd36;// -'a'+(10+26) |
else if (i_bits[5:0] == 6'd62) // An '@' sign |
o_char <= 8'h40; |
else // if (i_char == 6'h63) // A '%' sign |
o_char <= 8'h25; |
end |
|
always @(posedge i_clk) |
if ((o_stb)&&(~i_busy)) |
o_stb <= 1'b0; |
else if ((i_stb)&&(~o_stb)) |
o_stb <= 1'b1; |
|
assign o_busy = o_stb; |
|
endmodule |
|
/rtl/sdspi.v
0,0 → 1,1113
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: sdspi.v |
// |
// Project: SD-Card controller, using a shared SPI interface |
// |
// Purpose: |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`define SDSPI_CMD_ADDRESS 2'h0 |
`define SDSPI_DAT_ADDRESS 2'h1 |
`define SDSPI_FIFO_A_ADDR 2'h2 |
`define SDSPI_FIFO_B_ADDR 2'h3 |
// |
// `define SDSPI_CMD_ID 3'h0 |
// `define SDSPI_CMD_A1 3'h1 |
// `define SDSPI_CMD_A2 3'h2 |
// `define SDSPI_CMD_A3 3'h3 |
// `define SDSPI_CMD_A4 3'h4 |
// `define SDSPI_CMD_CRC 3'h5 |
// `define SDSPI_CMD_FIFO 3'h6 |
// `define SDSPI_CMD_WAIT 3'h7 |
// |
`define SDSPI_EXPECT_R1 2'b00 |
`define SDSPI_EXPECT_R1B 2'b01 |
`define SDSPI_EXPECT_R3 2'b10 |
// |
`define SDSPI_RSP_NONE 3'h0 // No response yet from device |
`define SDSPI_RSP_BSYWAIT 3'h1 // R1b, wait for device to send nonzero |
`define SDSPI_RSP_GETWORD 3'h2 // Get 32-bit data word from device |
`define SDSPI_RSP_GETTOKEN 3'h4 // Write to device, read from FIFO, wait for completion token |
`define SDSPI_RSP_WAIT_WHILE_BUSY 3'h5 // Read from device |
`define SDSPI_RSP_RDCOMPLETE 3'h6 |
`define SDSPI_RSP_WRITING 3'h7 // Read from device, write into FIFO |
// |
module sdspi(i_clk, |
// Wishbone interface |
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, |
o_wb_ack, o_wb_stall, o_wb_data, |
// SDCard interface |
o_cs_n, o_sck, o_mosi, i_miso, |
// Our interrupt |
o_int, |
// And whether or not we own the bus |
i_bus_grant, |
// And some wires for debugging it all |
o_debug); |
parameter LGFIFOLN = 7; |
input i_clk; |
// |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [1:0] i_wb_addr; |
input [31:0] i_wb_data; |
output reg o_wb_ack; |
output wire o_wb_stall; |
output reg [31:0] o_wb_data; |
// |
output wire o_cs_n, o_sck, o_mosi; |
input i_miso; |
// The interrupt |
output reg o_int; |
// .. and whether or not we can use the SPI port |
input i_bus_grant; |
// |
output wire [31:0] o_debug; |
|
// |
// Some WB simplifications: |
// |
reg r_cmd_busy; |
wire wb_stb, write_stb, cmd_stb; // read_stb |
assign wb_stb = ((i_wb_cyc)&&(i_wb_stb)&&(~o_wb_stall)); |
assign write_stb = ((wb_stb)&&( i_wb_we)); |
// assign read_stb = ((wb_stb)&&(~i_wb_we)); |
assign cmd_stb = (~r_cmd_busy)&&(write_stb) |
&&(i_wb_addr==`SDSPI_CMD_ADDRESS); |
|
|
// |
// Access to our lower-level SDSPI driver, the one that actually |
// uses/sets the SPI ports |
// |
reg [6:0] r_sdspi_clk; |
reg ll_cmd_stb; |
reg [7:0] ll_cmd_dat; |
wire ll_out_stb, ll_idle; |
wire [7:0] ll_out_dat; |
llsdspi lowlevel(i_clk, r_sdspi_clk, r_cmd_busy, ll_cmd_stb, ll_cmd_dat, |
o_cs_n, o_sck, o_mosi, i_miso, |
ll_out_stb, ll_out_dat, ll_idle, |
i_bus_grant); |
|
|
// CRC |
reg r_cmd_crc_stb; |
wire [7:0] cmd_crc; |
// State machine |
reg [2:0] r_cmd_state, r_rsp_state; |
// Current status, beyond state |
reg r_have_resp, r_use_fifo, r_fifo_wr, |
ll_fifo_rd_complete, ll_fifo_wr_complete, |
r_fifo_id, |
ll_fifo_wr, ll_fifo_rd, |
r_have_data_response_token, |
r_have_start_token; |
reg [7:0] fifo_byte; |
reg [7:0] r_last_r_one; |
// |
reg [31:0] r_data_reg; |
reg [1:0] r_data_fil, r_cmd_resp; |
// |
// |
wire need_reset; |
reg r_cmd_err; |
reg r_cmd_sent; |
reg [31:0] fifo_a_reg, fifo_b_reg; |
// |
reg q_busy; |
// |
reg [7:0] fifo_a_mem_0[0:((1<<LGFIFOLN)-1)], |
fifo_a_mem_1[0:((1<<LGFIFOLN)-1)], |
fifo_a_mem_2[0:((1<<LGFIFOLN)-1)], |
fifo_a_mem_3[0:((1<<LGFIFOLN)-1)], |
fifo_b_mem_0[0:((1<<LGFIFOLN)-1)], |
fifo_b_mem_1[0:((1<<LGFIFOLN)-1)], |
fifo_b_mem_2[0:((1<<LGFIFOLN)-1)], |
fifo_b_mem_3[0:((1<<LGFIFOLN)-1)]; |
reg [(LGFIFOLN-1):0] fifo_wb_addr; |
reg [(LGFIFOLN+1):0] rd_fifo_sd_addr; |
reg [(LGFIFOLN+1):0] wr_fifo_sd_addr; |
// |
reg [(LGFIFOLN+1):0] ll_fifo_addr; |
// |
reg fifo_crc_err; |
reg [1:0] ll_fifo_wr_state; |
reg [7:0] fifo_a_byte, fifo_b_byte; |
// |
reg [2:0] ll_fifo_pkt_state; |
reg fifo_rd_crc_stb, fifo_wr_crc_stb; |
// |
reg [3:0] fifo_rd_crc_count, fifo_wr_crc_count; |
reg [15:0] fifo_rd_crc_reg, fifo_wr_crc_reg; |
// |
reg [3:0] r_cmd_crc_cnt; |
reg [7:0] r_cmd_crc; |
// |
reg r_cmd_crc_ff; |
// |
reg [3:0] r_lgblklen; |
wire [3:0] max_lgblklen; |
assign max_lgblklen = LGFIFOLN; |
// |
reg [25:0] r_watchdog; |
reg r_watchdog_err; |
reg pre_cmd_state; |
|
// Relieve some stress from the WB bus timing |
reg new_data, new_cmd; |
reg [31:0] r_wb_data; |
always @(posedge i_clk) |
new_data <= (write_stb)&&(i_wb_addr == `SDSPI_DAT_ADDRESS); |
always @(posedge i_clk) |
new_cmd <= (~r_cmd_busy)&&(write_stb) |
&&(i_wb_addr==`SDSPI_CMD_ADDRESS); |
always @(posedge i_clk) |
r_wb_data <= i_wb_data; |
|
|
initial r_cmd_busy = 1'b0; |
initial r_data_reg = 32'h00; |
initial r_last_r_one = 8'hff; |
initial ll_cmd_stb = 1'b0; |
initial ll_fifo_rd = 1'b0; |
initial ll_fifo_wr = 1'b0; |
initial r_rsp_state = 3'h0; |
initial r_cmd_state = 3'h0; |
initial r_use_fifo = 1'b0; |
initial r_data_fil = 2'b00; |
initial r_lgblklen = LGFIFOLN; |
initial r_cmd_err = 1'b0; |
always @(posedge i_clk) |
begin |
if (~ll_cmd_stb) |
begin |
r_have_resp <= 1'b0; |
ll_fifo_wr <= 1'b0; |
ll_fifo_rd <= 1'b0; |
// r_rsp_state <= 3'h0; |
r_cmd_state <= 3'h0; |
r_use_fifo <= 1'b0; |
r_data_fil <= 2'b00; |
r_cmd_resp <= `SDSPI_EXPECT_R1; |
end |
|
r_cmd_crc_stb <= 1'b0; |
if (pre_cmd_state) |
begin // While we are actively sending data, and clocking the |
// interface, do: |
// |
// Here we use the transmit command state, or |
// r_cmd_state, to determine where we are at in this |
// process, and we use (ll_cmd_stb)&&(ll_idle) to |
// determine that we have sent a byte. ll_cmd_dat is |
// set here as well--it's the byte we wish to transmit. |
if (r_cmd_state == 3'h0) |
begin |
r_cmd_state <= r_cmd_state + 3'h1; |
ll_cmd_dat <= r_data_reg[31:24]; |
r_cmd_crc_stb <= 1'b1; |
end else if (r_cmd_state == 3'h1) |
begin |
r_cmd_state <= r_cmd_state + 3'h1; |
ll_cmd_dat <= r_data_reg[23:16]; |
r_cmd_crc_stb <= 1'b1; |
end else if (r_cmd_state == 3'h2) |
begin |
r_cmd_state <= r_cmd_state + 3'h1; |
ll_cmd_dat <= r_data_reg[15:8]; |
r_cmd_crc_stb <= 1'b1; |
end else if (r_cmd_state == 3'h3) |
begin |
r_cmd_state <= r_cmd_state + 3'h1; |
ll_cmd_dat <= r_data_reg[7:0]; |
r_cmd_crc_stb <= 1'b1; |
end else if (r_cmd_state == 3'h4) |
begin |
r_cmd_state <= r_cmd_state + 3'h1; |
ll_cmd_dat <= cmd_crc; |
end else if (r_cmd_state == 3'h5) |
begin |
ll_cmd_dat <= 8'hff; |
if (r_have_resp) |
begin |
if (r_use_fifo) |
r_cmd_state <= r_cmd_state + 3'h1; |
else |
r_cmd_state <= r_cmd_state + 3'h2; |
ll_fifo_rd <= (r_use_fifo)&&(r_fifo_wr); |
if ((r_use_fifo)&&(r_fifo_wr)) |
ll_cmd_dat <= 8'hfe; |
end |
end else if (r_cmd_state == 3'h6) |
begin |
ll_cmd_dat <= 8'hff; |
if (ll_fifo_rd_complete) |
begin // If we've finished reading from the |
// FIFO, then move on |
r_cmd_state <= r_cmd_state + 3'h1; |
ll_fifo_rd <= 1'b0; |
end else if (ll_fifo_rd) |
ll_cmd_dat <= fifo_byte; |
end else // if (r_cmd_state == 7) |
ll_cmd_dat <= 8'hff; |
|
|
// Here we handle the receive portion of the interface. |
// Note that the IF begins with an if of ll_out_stb. |
// That is, if a byte is ready from the lower level. |
// |
// Here we depend upon r_cmd_resp, the response we are |
// expecting from the SDCard, and r_rsp_state, the |
// state machine for where we are at receive what we |
// are expecting. |
if (pre_rsp_state) |
begin |
if (r_rsp_state == `SDSPI_RSP_NONE) |
begin // Waiting on R1 |
if (~ll_out_dat[7]) |
begin |
r_last_r_one <= ll_out_dat; |
if (r_cmd_resp == `SDSPI_EXPECT_R1) |
begin // Expecting R1 alone |
r_have_resp <= 1'b1; |
ll_cmd_stb <= (r_use_fifo); |
r_data_reg <= 32'hffffffff; |
ll_fifo_wr<=(r_use_fifo)&&(~r_fifo_wr); |
end else if (r_cmd_resp == `SDSPI_EXPECT_R1B) |
begin // Go wait on R1b |
r_data_reg <= 32'hffffffff; |
end // else wait on 32-bit rsp |
end |
end else if (r_rsp_state == `SDSPI_RSP_BSYWAIT) |
begin // Waiting on R1b, have R1 |
if (nonzero_out) |
r_have_resp <= 1'b1; |
ll_cmd_stb <= (r_use_fifo); |
end else if (r_rsp_state == `SDSPI_RSP_GETWORD) |
begin // Have R1, waiting on all of R2/R3/R7 |
r_data_reg <= { r_data_reg[23:0], ll_out_dat }; |
r_data_fil <= r_data_fil+2'b01; |
if (r_data_fil == 2'b11) |
begin |
ll_cmd_stb <= (r_use_fifo); |
// r_rsp_state <= 3'h3; |
end |
end else if (r_rsp_state == `SDSPI_RSP_WAIT_WHILE_BUSY) |
begin // Wait while device is busy writing |
// if (nonzero_out) |
// begin |
// r_data_reg[31:8] <= 24'h00; |
// r_data_reg[7:0] <= ll_out_dat; |
// // r_rsp_state <= 3'h6; |
// end |
; |
end else if (r_rsp_state == `SDSPI_RSP_RDCOMPLETE) |
begin // Block write command has completed |
ll_cmd_stb <= 1'b0; |
end else if (r_rsp_state == `SDSPI_RSP_WRITING) |
begin // We are reading from the device into |
// our FIFO |
if ((ll_fifo_wr_complete) |
// Or ... we receive an error |
||((~r_have_start_token) |
&&(~ll_out_dat[4]) |
&&(ll_out_dat[0]))) |
begin |
ll_fifo_wr <= 1'b0; |
ll_cmd_stb <= 1'b0; |
end |
end |
end |
|
if (r_watchdog_err) |
ll_cmd_stb <= 1'b0; |
r_cmd_err<= (r_cmd_err)|(fifo_crc_err)|(r_watchdog_err); |
end else if (r_cmd_busy) |
begin |
r_cmd_busy <= (ll_cmd_stb)||(~ll_idle); |
end else if (new_cmd) |
begin // Command write |
// Clear the error on any write, whether a commanding |
// one or not. -- provided the user requests clearing |
// it (by setting the bit high) |
r_cmd_err <= (r_cmd_err)&&(~r_wb_data[15]); |
// In a similar fashion, we can switch fifos even if |
// not in the middle of a command |
r_fifo_id <= r_wb_data[12]; |
// |
// Doesn't matter what this is set to as long as we |
// aren't busy, so we can set it irrelevantly here. |
ll_cmd_dat <= r_wb_data[7:0]; |
// |
// Note that we only issue a write upon receiving a |
// valid command. Such a command is 8 bits, and must |
// start with its high order bits set to zero and one. |
// Hence ... we test for that here. |
if (r_wb_data[7:6] == 2'b01) |
begin // Issue a command |
// |
r_cmd_busy <= 1'b1; |
// |
ll_cmd_stb <= 1'b1; |
r_cmd_resp <= r_wb_data[9:8]; |
// |
r_cmd_crc_stb <= 1'b1; |
// |
r_fifo_wr <= r_wb_data[10]; |
r_use_fifo <= r_wb_data[11]; |
// |
end else if (r_wb_data[7]) |
// If, on the other hand, the command was invalid, |
// then it must have been an attempt to read our |
// internal configuration. So we'll place that on |
// our data register. |
r_data_reg <= { 8'h00, |
4'h0, max_lgblklen, |
4'h0, r_lgblklen, 1'b0, r_sdspi_clk }; |
end else if (new_data) // Data write |
r_data_reg <= r_wb_data; |
end |
|
|
always @(posedge i_clk) |
pre_cmd_state <= (ll_cmd_stb)&&(ll_idle); |
|
reg ready_for_response_token; |
always @(posedge i_clk) |
if (~r_cmd_busy) |
ready_for_response_token <= 1'b0; |
else if (ll_fifo_rd) |
ready_for_response_token <= 1'b1; |
always @(posedge i_clk) |
if (~r_cmd_busy) |
r_have_data_response_token <= 1'b0; |
else if ((ll_out_stb)&&(ready_for_response_token)&&(~ll_out_dat[4])) |
r_have_data_response_token <= 1'b1; |
|
reg [2:0] second_rsp_state; |
always @(posedge i_clk) |
if((r_cmd_resp == `SDSPI_EXPECT_R1)&&(r_use_fifo)&&(r_fifo_wr)) |
second_rsp_state <= `SDSPI_RSP_GETTOKEN; |
else if (r_cmd_resp == `SDSPI_EXPECT_R1) |
second_rsp_state <= `SDSPI_RSP_WRITING; |
else if (r_cmd_resp == `SDSPI_EXPECT_R1B) |
second_rsp_state <= `SDSPI_RSP_BSYWAIT; |
else |
second_rsp_state <= `SDSPI_RSP_GETWORD; |
|
reg pre_rsp_state, nonzero_out; |
always @(posedge i_clk) |
if (ll_out_stb) |
nonzero_out <= (|ll_out_dat); |
always @(posedge i_clk) |
pre_rsp_state <= (ll_out_stb)&&(r_cmd_sent); |
|
// Each bit depends upon 8 bits of input |
initial r_rsp_state = 3'h0; |
always @(posedge i_clk) |
if (~r_cmd_sent) |
r_rsp_state <= 3'h0; |
else if (pre_rsp_state) |
begin |
if ((r_rsp_state == `SDSPI_RSP_NONE)&&(~ll_out_dat[7])) |
begin |
r_rsp_state <= second_rsp_state; |
end else if (r_rsp_state == `SDSPI_RSP_BSYWAIT) |
begin // Waiting on R1b, have R1 |
// R1b never uses the FIFO |
if (nonzero_out) |
r_rsp_state <= 3'h6; |
end else if (r_rsp_state == `SDSPI_RSP_GETWORD) |
begin // Have R1, waiting on all of R2/R3/R7 |
if (r_data_fil == 2'b11) |
r_rsp_state <= `SDSPI_RSP_RDCOMPLETE; |
end else if (r_rsp_state == `SDSPI_RSP_GETTOKEN) |
begin // Wait on data token response |
if (r_have_data_response_token) |
r_rsp_state <= `SDSPI_RSP_WAIT_WHILE_BUSY; |
end else if (r_rsp_state == `SDSPI_RSP_WAIT_WHILE_BUSY) |
begin // Wait while device is busy writing |
if (nonzero_out) |
r_rsp_state <= `SDSPI_RSP_RDCOMPLETE; |
end |
//else if (r_rsp_state == 3'h6) |
//begin // Block write command has completed |
// // ll_cmd_stb <= 1'b0; |
// end else if (r_rsp_state == 3'h7) |
// begin // We are reading from the device into |
// // our FIFO |
// end |
end |
|
always @(posedge i_clk) |
r_cmd_sent <= (ll_cmd_stb)&&(r_cmd_state >= 3'h5); |
|
// initial r_sdspi_clk = 6'h3c; |
initial r_sdspi_clk = 7'h63; |
always @(posedge i_clk) |
begin |
// Update our internal configuration parameters, unconnected |
// with the card. These include the speed of the interface, |
// and the size of the block length to expect as part of a FIFO |
// command. |
if ((new_cmd)&&(r_wb_data[7:6]==2'b11)&&(~r_data_reg[7]) |
&&(r_data_reg[15:12]==4'h00)) |
begin |
if (|r_data_reg[6:0]) |
r_sdspi_clk <= r_data_reg[6:0]; |
if (|r_data_reg[11:8]) |
r_lgblklen <= r_data_reg[11:8]; |
end if (r_lgblklen > max_lgblklen) |
r_lgblklen <= max_lgblklen; |
end |
|
assign need_reset = 1'b0; |
always @(posedge i_clk) |
case(i_wb_addr) |
`SDSPI_CMD_ADDRESS: |
o_wb_data <= { need_reset, 11'h00, |
3'h0, fifo_crc_err, |
r_cmd_err, r_cmd_busy, 1'b0, r_fifo_id, |
r_use_fifo, r_fifo_wr, r_cmd_resp, |
r_last_r_one }; |
`SDSPI_DAT_ADDRESS: |
o_wb_data <= r_data_reg; |
`SDSPI_FIFO_A_ADDR: |
o_wb_data <= fifo_a_reg; |
`SDSPI_FIFO_B_ADDR: |
o_wb_data <= fifo_b_reg; |
endcase |
|
always @(posedge i_clk) |
o_wb_ack <= wb_stb; |
|
initial q_busy = 1'b1; |
always @(posedge i_clk) |
q_busy <= r_cmd_busy; |
always @(posedge i_clk) |
o_int <= (~r_cmd_busy)&&(q_busy); |
|
assign o_wb_stall = 1'b0; |
|
// |
// Let's work with our FIFO memory here ... |
// |
// |
always @(posedge i_clk) |
begin |
if ((write_stb)&&(i_wb_addr == `SDSPI_CMD_ADDRESS)) |
begin // Command write |
// Clear the read/write address |
fifo_wb_addr <= {(LGFIFOLN){1'b0}}; |
end else if ((wb_stb)&&(i_wb_addr[1])) |
begin // On read or write, of either FIFO, |
// we increase our pointer |
fifo_wb_addr <= fifo_wb_addr + 1; |
// And let ourselves know we need to update ourselves |
// on the next clock |
end |
end |
|
// Prepare reading of the FIFO for the WB bus read |
// Memory read #1 |
always @(posedge i_clk) |
begin |
fifo_a_reg <= { |
fifo_a_mem_0[ fifo_wb_addr ], |
fifo_a_mem_1[ fifo_wb_addr ], |
fifo_a_mem_2[ fifo_wb_addr ], |
fifo_a_mem_3[ fifo_wb_addr ] }; |
fifo_b_reg <= { |
fifo_b_mem_0[ fifo_wb_addr ], |
fifo_b_mem_1[ fifo_wb_addr ], |
fifo_b_mem_2[ fifo_wb_addr ], |
fifo_b_mem_3[ fifo_wb_addr ] }; |
end |
|
// Okay, now ... writing our FIFO ... |
reg pre_fifo_addr_inc_rd; |
reg pre_fifo_addr_inc_wr; |
initial pre_fifo_addr_inc_rd = 1'b0; |
initial pre_fifo_addr_inc_wr = 1'b0; |
always @(posedge i_clk) |
pre_fifo_addr_inc_wr <= ((ll_fifo_wr)&&(ll_out_stb)&&(r_have_start_token)); |
always @(posedge i_clk) |
pre_fifo_addr_inc_rd <= ((ll_fifo_rd)&&(ll_cmd_stb)&&(ll_idle));//&&(ll_fifo_pkt_state[2:0]!=3'b000)); |
always @(posedge i_clk) |
begin |
// if ((write_stb)&&(i_wb_addr == `SDSPI_CMD_ADDRESS)&&(i_wb_data[11])) |
// ll_fifo_addr <= {(LGFIFOLN+2){1'b0}}; |
if (~r_cmd_busy) |
ll_fifo_addr <= {(LGFIFOLN+2){1'b0}}; |
else if ((pre_fifo_addr_inc_wr)||(pre_fifo_addr_inc_rd)) |
ll_fifo_addr <= ll_fifo_addr + 1; |
end |
|
// Look for that start token |
always @(posedge i_clk) |
if (~r_cmd_busy) |
r_have_start_token <= 1'b0; |
else if ((ll_fifo_wr)&&(ll_out_stb)&&(ll_out_dat==8'hfe)) |
r_have_start_token <= 1'b1; |
|
reg last_fifo_byte; |
initial last_fifo_byte = 1'b0; |
always @(posedge i_clk) |
if (ll_fifo_wr) |
last_fifo_byte <= (ll_fifo_addr == w_blklimit); |
else |
last_fifo_byte <= 1'b0; |
|
// This is the one (and only allowed) write to the FIFO memory always |
// block. |
// |
// If ll_fifo_wr is true, we'll be writing to the FIFO, and we'll do |
// that here. This is different from r_fifo_wr, which specifies that |
// we will be writing to the SDCard from the FIFO, and hence READING |
// from the FIFO. |
// |
reg pre_fifo_a_wr, pre_fifo_b_wr, pre_fifo_crc_a, pre_fifo_crc_b, |
clear_fifo_crc; |
always @(posedge i_clk) |
begin |
pre_fifo_a_wr <= (ll_fifo_wr)&&(ll_out_stb)&&(~r_fifo_id)&&(ll_fifo_wr_state == 2'b00); |
pre_fifo_b_wr <= (ll_fifo_wr)&&(ll_out_stb)&&( r_fifo_id)&&(ll_fifo_wr_state == 2'b00); |
fifo_wr_crc_stb <= (ll_fifo_wr)&&(ll_out_stb)&&(ll_fifo_wr_state == 2'b00)&&(r_have_start_token); |
pre_fifo_crc_a<= (ll_fifo_wr)&&(ll_out_stb)&&(ll_fifo_wr_state == 2'b01); |
pre_fifo_crc_b<= (ll_fifo_wr)&&(ll_out_stb)&&(ll_fifo_wr_state == 2'b10); |
clear_fifo_crc <= (new_cmd)&&(r_wb_data[15]); |
end |
|
reg fifo_a_wr, fifo_b_wr; |
reg [3:0] fifo_a_wr_mask, fifo_b_wr_mask; |
reg [(LGFIFOLN-1):0] fifo_a_wr_addr, fifo_b_wr_addr; |
reg [31:0] fifo_a_wr_data, fifo_b_wr_data; |
|
initial fifo_crc_err = 1'b0; |
always @(posedge i_clk) |
begin // One and only memory write allowed |
fifo_a_wr <= 1'b0; |
fifo_a_wr_data <= { ll_out_dat, ll_out_dat, ll_out_dat, ll_out_dat }; |
if ((write_stb)&&(i_wb_addr[1:0]==2'b10)) |
begin |
fifo_a_wr <= 1'b1; |
fifo_a_wr_mask <= 4'b1111; |
fifo_a_wr_addr <= fifo_wb_addr; |
fifo_a_wr_data <= i_wb_data; |
end else if (pre_fifo_a_wr) |
begin |
fifo_a_wr <= 1'b1; |
fifo_a_wr_addr <= ll_fifo_addr[(LGFIFOLN+1):2]; |
case(ll_fifo_addr[1:0]) |
2'b00: fifo_a_wr_mask <= 4'b0001; |
2'b01: fifo_a_wr_mask <= 4'b0010; |
2'b10: fifo_a_wr_mask <= 4'b0100; |
2'b11: fifo_a_wr_mask <= 4'b1000; |
endcase |
end |
|
if ((fifo_a_wr)&&(fifo_a_wr_mask[0])) |
fifo_a_mem_0[fifo_a_wr_addr] <= fifo_a_wr_data[7:0]; |
if ((fifo_a_wr)&&(fifo_a_wr_mask[1])) |
fifo_a_mem_1[fifo_a_wr_addr] <= fifo_a_wr_data[15:8]; |
if ((fifo_a_wr)&&(fifo_a_wr_mask[2])) |
fifo_a_mem_2[fifo_a_wr_addr] <= fifo_a_wr_data[23:16]; |
if ((fifo_a_wr)&&(fifo_a_wr_mask[3])) |
fifo_a_mem_3[fifo_a_wr_addr] <= fifo_a_wr_data[31:24]; |
|
fifo_b_wr <= 1'b0; |
fifo_b_wr_data <= { ll_out_dat, ll_out_dat, ll_out_dat, ll_out_dat }; |
if ((write_stb)&&(i_wb_addr[1:0]==2'b11)) |
begin |
fifo_b_wr <= 1'b1; |
fifo_b_wr_mask <= 4'b1111; |
fifo_b_wr_addr <= fifo_wb_addr; |
fifo_b_wr_data <= i_wb_data; |
end else if (pre_fifo_b_wr) |
begin |
fifo_b_wr <= 1'b1; |
fifo_b_wr_addr <= ll_fifo_addr[(LGFIFOLN+1):2]; |
case(ll_fifo_addr[1:0]) |
2'b00: fifo_b_wr_mask <= 4'b0001; |
2'b01: fifo_b_wr_mask <= 4'b0010; |
2'b10: fifo_b_wr_mask <= 4'b0100; |
2'b11: fifo_b_wr_mask <= 4'b1000; |
endcase |
end |
|
if ((fifo_b_wr)&&(fifo_b_wr_mask[0])) |
fifo_b_mem_0[fifo_b_wr_addr] <= fifo_b_wr_data[7:0]; |
if ((fifo_b_wr)&&(fifo_b_wr_mask[1])) |
fifo_b_mem_1[fifo_b_wr_addr] <= fifo_b_wr_data[15:8]; |
if ((fifo_b_wr)&&(fifo_b_wr_mask[2])) |
fifo_b_mem_2[fifo_b_wr_addr] <= fifo_b_wr_data[23:16]; |
if ((fifo_b_wr)&&(fifo_b_wr_mask[3])) |
fifo_b_mem_3[fifo_b_wr_addr] <= fifo_b_wr_data[31:24]; |
|
if (~r_cmd_busy) |
ll_fifo_wr_complete <= 1'b0; |
|
if (~r_cmd_busy) |
ll_fifo_wr_state <= 2'b00; |
else if ((pre_fifo_a_wr)||(pre_fifo_b_wr)) |
ll_fifo_wr_state <= (last_fifo_byte)? 2'b01:2'b00; |
|
if (pre_fifo_crc_a) |
begin |
fifo_crc_err <= fifo_crc_err | (fifo_wr_crc_reg[15:8]!=ll_out_dat); |
ll_fifo_wr_state <= ll_fifo_wr_state + 2'b01; |
end if (pre_fifo_crc_b) |
begin |
fifo_crc_err <= fifo_crc_err | (fifo_wr_crc_reg[7:0]!=ll_out_dat); |
ll_fifo_wr_state <= ll_fifo_wr_state + 2'b01; |
ll_fifo_wr_complete <= 1'b1; |
end else if (clear_fifo_crc) |
fifo_crc_err <= 1'b0; |
end |
|
always @(posedge i_clk) |
begin // Second memory read, this time for the FIFO |
case(ll_fifo_addr[1:0]) |
2'b00: begin |
fifo_a_byte<=fifo_a_mem_0[ll_fifo_addr[(LGFIFOLN+1):2]]; |
fifo_b_byte<=fifo_b_mem_0[ll_fifo_addr[(LGFIFOLN+1):2]]; |
end |
2'b01: begin |
fifo_a_byte<=fifo_a_mem_1[ll_fifo_addr[(LGFIFOLN+1):2]]; |
fifo_b_byte<=fifo_b_mem_1[ll_fifo_addr[(LGFIFOLN+1):2]]; |
end |
2'b10: begin |
fifo_a_byte<=fifo_a_mem_2[ll_fifo_addr[(LGFIFOLN+1):2]]; |
fifo_b_byte<=fifo_b_mem_2[ll_fifo_addr[(LGFIFOLN+1):2]]; |
end |
2'b11: begin |
fifo_a_byte<=fifo_a_mem_3[ll_fifo_addr[(LGFIFOLN+1):2]]; |
fifo_b_byte<=fifo_b_mem_3[ll_fifo_addr[(LGFIFOLN+1):2]]; |
end |
endcase |
end |
|
reg [(LGFIFOLN-1):0] r_blklimit; |
wire [(LGFIFOLN+1):0] w_blklimit; |
always @(posedge i_clk) |
r_blklimit[(LGFIFOLN-1):0] = (1<<r_lgblklen)-1; |
assign w_blklimit = { r_blklimit, 2'b11 }; |
|
// Package the FIFO reads up into a packet |
always @(posedge i_clk) |
begin |
fifo_rd_crc_stb <= 1'b0; |
if (r_cmd_busy) |
begin |
if (ll_fifo_pkt_state[2:0] == 3'b000) |
begin |
if((ll_fifo_rd)&&(ll_cmd_stb)&&(ll_idle)) |
begin |
ll_fifo_pkt_state <= ll_fifo_pkt_state + 3'b001; |
end |
end else if (ll_fifo_pkt_state[2:0] == 3'b001) |
begin |
if((ll_fifo_rd)&&(ll_cmd_stb)&&(ll_idle)) |
begin |
ll_fifo_pkt_state <= ll_fifo_pkt_state + 3'b001; |
fifo_byte <= (r_fifo_id) |
? fifo_b_byte : fifo_a_byte; |
fifo_rd_crc_stb <= 1'b1; |
end |
end else if (ll_fifo_pkt_state[2:0] == 3'b010) |
begin |
if((ll_fifo_rd)&&(ll_cmd_stb)&&(ll_idle)) |
begin |
fifo_byte <= (r_fifo_id) |
? fifo_b_byte : fifo_a_byte; |
fifo_rd_crc_stb <= 1'b1; |
end |
if (ll_fifo_addr == 0) |
ll_fifo_pkt_state <= 3'b011; |
end else if (ll_fifo_pkt_state == 3'b011) |
begin // 1st CRC byte |
if((ll_fifo_rd)&&(ll_cmd_stb)&&(ll_idle)) |
begin |
fifo_byte <= fifo_rd_crc_reg[15:8]; |
ll_fifo_pkt_state <= 3'b100; |
end |
end else if (ll_fifo_pkt_state == 3'b100) |
begin // 2nd CRC byte |
if((ll_fifo_rd)&&(ll_cmd_stb)&&(ll_idle)) |
begin |
fifo_byte <= fifo_rd_crc_reg[7:0]; |
ll_fifo_pkt_state <= 3'b101; |
end |
end else if((ll_fifo_rd)&&(ll_cmd_stb)&&(ll_idle)) |
begin |
// Idle the channel |
ll_fifo_rd_complete <= 1'b1; |
fifo_byte <= 8'hff; |
end |
end else if ((write_stb)&&(i_wb_addr == `SDSPI_CMD_ADDRESS)) |
begin |
ll_fifo_pkt_state <= 3'h0; |
ll_fifo_rd_complete <= 1'b0; |
fifo_byte <= (i_wb_data[12]) ? fifo_b_byte : fifo_a_byte; |
fifo_rd_crc_stb <= 1'b1; |
end else begin // Packet state is IDLE (clear the CRC registers) |
ll_fifo_pkt_state <= 3'b111; |
ll_fifo_rd_complete <= 1'b1; |
end |
end |
|
always @(posedge i_clk) |
begin |
if (~ll_fifo_wr) |
fifo_wr_crc_reg <= 16'h00; |
else if (fifo_wr_crc_stb) |
begin |
fifo_wr_crc_reg[15:8] <=fifo_wr_crc_reg[15:8]^ll_out_dat; |
fifo_wr_crc_count <= 4'h8; |
end else if (|fifo_wr_crc_count) |
begin |
fifo_wr_crc_count <= fifo_wr_crc_count - 4'h1; |
if (fifo_wr_crc_reg[15]) |
fifo_wr_crc_reg <= { fifo_wr_crc_reg[14:0], 1'b0 } |
^ 16'h1021; |
else |
fifo_wr_crc_reg <= { fifo_wr_crc_reg[14:0], 1'b0 }; |
end |
end |
|
always @(posedge i_clk) |
begin |
if (~r_cmd_busy) |
begin |
fifo_rd_crc_reg <= 16'h00; |
fifo_rd_crc_count <= 4'h0; |
end else if (fifo_rd_crc_stb) |
begin |
fifo_rd_crc_reg[15:8] <=fifo_rd_crc_reg[15:8]^fifo_byte; |
fifo_rd_crc_count <= 4'h8; |
end else if (|fifo_rd_crc_count) |
begin |
fifo_rd_crc_count <= fifo_rd_crc_count - 4'h1; |
if (fifo_rd_crc_reg[15]) |
fifo_rd_crc_reg <= { fifo_rd_crc_reg[14:0], 1'b0 } |
^ 16'h1021; |
else |
fifo_rd_crc_reg <= { fifo_rd_crc_reg[14:0], 1'b0 }; |
end |
end |
|
// |
// Calculate a CRC for the command section of our output |
// |
initial r_cmd_crc_ff = 1'b0; |
always @(posedge i_clk) |
begin |
if (~r_cmd_busy) |
begin |
r_cmd_crc <= 8'h00; |
r_cmd_crc_cnt <= 4'hf; |
r_cmd_crc_ff <= 1'b0; |
end else if (~r_cmd_crc_cnt[3]) |
begin |
r_cmd_crc_cnt <= r_cmd_crc_cnt - 4'h1; |
if (r_cmd_crc[7]) |
r_cmd_crc <= { r_cmd_crc[6:0], 1'b0 } ^ 8'h12; |
else |
r_cmd_crc <= { r_cmd_crc[6:0], 1'b0 }; |
r_cmd_crc_ff <= (r_cmd_crc_ff)||(r_cmd_crc_stb); |
end else if ((r_cmd_crc_stb)||(r_cmd_crc_ff)) |
begin |
r_cmd_crc <= r_cmd_crc ^ ll_cmd_dat; |
r_cmd_crc_cnt <= 4'h7; |
r_cmd_crc_ff <= 1'b0; |
end |
end |
assign cmd_crc = { r_cmd_crc[7:1], 1'b1 }; |
|
// |
// Some watchdog logic for us. This way, if we are waiting for the |
// card to respond, and something goes wrong, we can timeout the |
// transaction and ... figure out what to do about it later. At least |
// we'll have an error indication. |
// |
initial r_watchdog = 26'h3ffffff; |
initial r_watchdog_err = 1'b0; |
always @(posedge i_clk) |
if (~r_cmd_busy) |
r_watchdog_err <= 1'b0; |
else if (r_watchdog == 0) |
r_watchdog_err <= 1'b1; |
always @(posedge i_clk) |
if (~r_cmd_busy) |
r_watchdog <= 26'h3fffff; |
else if (|r_watchdog) |
r_watchdog <= r_watchdog - 26'h1; |
|
assign o_debug = { ((ll_cmd_stb)&&(ll_idle))||(ll_out_stb), |
ll_cmd_stb, ll_idle, ll_out_stb, // 4'h |
o_cs_n, o_sck, o_mosi, i_miso, // 4'h |
r_cmd_state, i_bus_grant, // 4'h |
r_rsp_state, r_cmd_busy, // 4'h |
ll_cmd_dat, // 8'b |
ll_out_dat }; // 8'b |
endmodule |
|
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: llsdspi.v |
// |
// Project: SD-Card controller, using a shared SPI interface |
// |
// Purpose: This file implements the "lower-level" interface to the |
// SD-Card controller. Specifically, it turns byte-level |
// interaction requests into SPI bit-wise interactions. Further, it |
// handles the request and grant for the SPI wires (i.e., it requests |
// the SPI port by pulling o_cs_n low, and then waits for i_bus_grant |
// to be true before continuing.). Finally, the speed/clock rate of the |
// communication is adjustable as a division of the current clock rate. |
// |
// i_speed |
// This is the number of clocks (minus one) between SPI clock |
// transitions. Hence a '0' (not tested, doesn't work) would |
// result in a SPI clock that alternated on every input clock |
// equivalently dividing the input clock by two, whereas a '1' |
// would divide the input clock by four. |
// |
// In general, the SPI clock frequency will be given by the |
// master clock frequency divided by twice this number plus one. |
// In other words, |
// |
// SPIFREQ=(i_clk FREQ) / (2*(i_speed+1)) |
// |
// i_stb |
// True if the master controller is requesting to send a byte. |
// This will be ignored unless o_idle is false. |
// |
// i_byte |
// The byte that the master controller wishes to send across the |
// interface. |
// |
// (The external SPI interface) |
// |
// o_stb |
// Only true for one clock--when a byte is valid coming in from the |
// interface, this will be true for one clock (a strobe) indicating |
// that a valid byte is ready to be read. |
// |
// o_byte |
// The value of the byte coming in. |
// |
// o_idle |
// True if this low-level device handler is ready to accept a |
// byte from the incoming interface, false otherwise. |
// |
// i_bus_grant |
// True if the SPI bus has been granted to this interface, false |
// otherwise. This has been placed here so that the interface of |
// the XuLA2 board may be shared between SPI-Flash and the SPI |
// based SDCard. An external arbiter will determine which of the |
// two gets to control the clock and mosi outputs given their |
// cs_n requests. If control is not granted, i_bus_grant will |
// remain low as will the actual cs_n going out of the FPGA. |
// |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`define LLSDSPI_IDLE 4'h0 |
`define LLSDSPI_HOTIDLE 4'h1 |
`define LLSDSPI_WAIT 4'h2 |
`define LLSDSPI_START 4'h3 |
// |
module llsdspi(i_clk, i_speed, i_cs, i_stb, i_byte, |
o_cs_n, o_sclk, o_mosi, i_miso, |
o_stb, o_byte, o_idle, i_bus_grant); |
parameter SPDBITS = 7; |
// |
input i_clk; |
// Parameters/setup |
input [(SPDBITS-1):0] i_speed; |
// The incoming interface |
input i_cs; |
input i_stb; |
input [7:0] i_byte; |
// The actual SPI interface |
output reg o_cs_n, o_sclk, o_mosi; |
input i_miso; |
// The outgoing interface |
output reg o_stb; |
output reg [7:0] o_byte; |
output wire o_idle; |
// And whether or not we actually own the interface (yet) |
input i_bus_grant; |
|
reg r_z_counter; |
reg [(SPDBITS-1):0] r_clk_counter; |
reg r_idle; |
reg [3:0] r_state; |
reg [7:0] r_byte, r_ireg; |
|
wire byte_accepted; |
assign byte_accepted = (i_stb)&&(o_idle); |
|
initial r_clk_counter = 7'h0; |
always @(posedge i_clk) |
begin |
if ((~i_cs)||(~i_bus_grant)) |
r_clk_counter <= 0; |
else if (byte_accepted) |
r_clk_counter <= i_speed; |
else if (~r_z_counter) |
r_clk_counter <= (r_clk_counter - {{(SPDBITS-1){1'b0}},1'b1}); |
else if ((r_state != `LLSDSPI_IDLE)&&(r_state != `LLSDSPI_HOTIDLE)) |
r_clk_counter <= (i_speed); |
// else |
// r_clk_counter <= 16'h00; |
end |
|
initial r_z_counter = 1'b1; |
always @(posedge i_clk) |
begin |
if ((~i_cs)||(~i_bus_grant)) |
r_z_counter <= 1'b1; |
else if (byte_accepted) |
r_z_counter <= 1'b0; |
else if (~r_z_counter) |
r_z_counter <= (r_clk_counter == 1); |
else if ((r_state != `LLSDSPI_IDLE)&&(r_state != `LLSDSPI_HOTIDLE)) |
r_z_counter <= 1'b0; |
end |
|
initial r_state = `LLSDSPI_IDLE; |
always @(posedge i_clk) |
begin |
o_stb <= 1'b0; |
o_cs_n <= ~i_cs; |
if (~i_cs) |
begin |
r_state <= `LLSDSPI_IDLE; |
r_idle <= 1'b0; |
o_sclk <= 1'b1; |
end else if (~r_z_counter) |
begin |
r_idle <= 1'b0; |
if (byte_accepted) |
begin // Will only happen within a hot idle state |
r_byte <= { i_byte[6:0], 1'b1 }; |
r_state <= `LLSDSPI_START+1; |
o_mosi <= i_byte[7]; |
end |
end else if (r_state == `LLSDSPI_IDLE) |
begin |
o_sclk <= 1'b1; |
if (byte_accepted) |
begin |
r_byte <= i_byte[7:0]; |
r_state <= (i_bus_grant)?`LLSDSPI_START:`LLSDSPI_WAIT; |
r_idle <= 1'b0; |
o_mosi <= i_byte[7]; |
end else begin |
r_idle <= 1'b1; |
end |
end else if (r_state == `LLSDSPI_WAIT) |
begin |
r_idle <= 1'b0; |
if (i_bus_grant) |
r_state <= `LLSDSPI_START; |
end else if (r_state == `LLSDSPI_HOTIDLE) |
begin |
// The clock is low, the bus is granted, we're just |
// waiting for the next byte to transmit |
o_sclk <= 1'b0; |
if (byte_accepted) |
begin |
r_byte <= i_byte[7:0]; |
r_state <= `LLSDSPI_START; |
r_idle <= 1'b0; |
o_mosi <= i_byte[7]; |
end else |
r_idle <= 1'b1; |
// end else if (r_state == `LLSDSPI_START) |
// begin |
// o_sclk <= 1'b0; |
// r_state <= r_state + 1; |
end else if (o_sclk) |
begin |
o_mosi <= r_byte[7]; |
r_byte <= { r_byte[6:0], 1'b1 }; |
r_state <= r_state + 1; |
o_sclk <= 1'b0; |
if (r_state >= `LLSDSPI_START+8) |
begin |
r_state <= `LLSDSPI_HOTIDLE; |
r_idle <= 1'b1; |
o_stb <= 1'b1; |
o_byte <= r_ireg; |
end else |
r_state <= r_state + 1; |
end else begin |
r_ireg <= { r_ireg[6:0], i_miso }; |
o_sclk <= 1'b1; |
end |
end |
|
assign o_idle = (r_idle)&&( (i_cs)&&(i_bus_grant) ); |
endmodule |
|
|
/rtl/wbudecompress.v
0,0 → 1,150
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbudecompress.v |
// |
// Project: FPGA library |
// |
// Purpose: Compression via this interface is simply a lookup table. |
// When writing, if requested, rather than writing a new 36-bit |
// word, we may be asked to repeat a word that's been written recently. |
// That's the goal of this routine: if given a word's (relative) address |
// in the write stream, we use that address, else we expect a full 32-bit |
// word to come in to be written. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
module wbudecompress(i_clk, i_stb, i_word, o_stb, o_word); |
input i_clk, i_stb; |
input [35:0] i_word; |
output reg o_stb; |
output reg [35:0] o_word; |
|
|
// Clock zero |
// { o_stb, r_stb } = 0 |
wire cmd_write_not_compressed = (i_word[35:33] == 3'h3); |
|
|
// Clock one: { o_stb, r_stb } = 4'h1 when done |
reg [7:0] wr_addr; |
initial wr_addr = 8'h0; |
always @(posedge i_clk) |
if ((i_stb)&&(cmd_write_not_compressed)) |
wr_addr <= wr_addr + 8'h1; |
|
reg [31:0] compression_tbl [0:255]; |
always @(posedge i_clk) |
if (i_stb) |
compression_tbl[wr_addr] <= { i_word[32:31], i_word[29:0] }; |
|
reg [35:0] r_word; |
always @(posedge i_clk) |
if (i_stb) |
r_word <= i_word; |
|
|
// Clock two, calculate the table address ... 1 is the smallest address |
// { o_stb, r_stb } = 4'h2 when done |
reg [7:0] cmd_addr; |
always @(posedge i_clk) |
cmd_addr = wr_addr - { r_word[32:31], r_word[29:24] }; |
|
// Let's also calculate the address, in case this is a compressed |
// address word |
reg [24:0] r_addr; |
always @(posedge i_clk) |
case(r_word[32:30]) |
3'b000: r_addr <= { 19'h0, r_word[29:24] }; |
3'b010: r_addr <= { 13'h0, r_word[29:18] }; |
3'b100: r_addr <= { 7'h0, r_word[29:12] }; |
3'b110: r_addr <= { 1'h0, r_word[29: 6] }; |
3'b001: r_addr <= { {(19){ r_word[29]}}, r_word[29:24] }; |
3'b011: r_addr <= { {(13){ r_word[29]}}, r_word[29:18] }; |
3'b101: r_addr <= { {( 7){ r_word[29]}}, r_word[29:12] }; |
3'b111: r_addr <= { {( 1){ r_word[29]}}, r_word[29: 6] }; |
endcase |
wire [31:0] w_addr; |
assign w_addr = { {(7){r_addr[24]}}, r_addr }; |
|
reg [9:0] rd_len; |
always @(posedge i_clk) |
if (~r_word[34]) |
rd_len <= 10'h01 + { 6'h00, r_word[33:31] }; |
else |
rd_len <= 10'h09 + { 1'b0, r_word[33:31], r_word[29:24] }; |
|
// Clock three, read the table value |
// { o_stb, r_stb } = 4'h4 when done |
// Maintaining ... |
// r_word (clock 1) |
// r_addr, rd_len (clock 2) |
reg [31:0] cword; |
always @(posedge i_clk) |
cword <= compression_tbl[cmd_addr]; |
|
|
// Pipeline the strobe signal to create an output strobe, 3 clocks later |
reg [2:0] r_stb; |
initial r_stb = 0; |
always @(posedge i_clk) |
r_stb <= { r_stb[1:0], i_stb }; |
|
// Clock four, now that the table value is valid, let's set our output |
// word. |
// { o_stb, r_stb } = 4'h8 when done |
always @(posedge i_clk) |
o_stb <= r_stb[2]; |
// Maintaining ... |
// r_word (clock 1) |
// r_addr, rd_len (clock 2) |
// cword (clock 3) |
// Any/all of these can be pipelined for faster operation |
// However, speed is really limited by the speed of the I/O port. At |
// it's fastest, it's 1 bit per clock, 48 clocks per codeword therefore, |
// thus ... things will hold still for much longer than just 5 clocks. |
always @(posedge i_clk) |
if (r_word[35:30] == 6'b101110) |
o_word <= r_word; |
else casez(r_word[35:30]) |
// Set address from something compressed ... unsigned |
6'b001??0: o_word <= { 4'h0, w_addr[31:0] }; |
// Set a new address as a signed offset from the last (set) one |
// (The last address is kept further down the chain, |
// we just mark here that the address is to be set |
// relative to it, and by how much.) |
6'b001??1: o_word <= { 3'h1, w_addr[31:30], 1'b1, w_addr[29:0]}; |
// Write a value to the bus, with the value given from our |
// codeword table |
6'b010???: o_word <= |
{ 3'h3, cword[31:30], r_word[30], cword[29:0] }; |
// Read, highly compressed length (1 word) |
6'b10????: o_word <= { 5'b11000, r_word[30], 20'h00, rd_len }; |
// Read, two word (3+9 bits) length |
6'b11????: o_word <= { 5'b11000, r_word[30], 20'h00, rd_len }; |
default: o_word <= r_word; |
endcase |
endmodule |
|
/rtl/cpu/fastops.v
0,0 → 1,304
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: fastops.v |
// |
// 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). |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
module fastops(i_clk,i_rst, i_ce, i_valid, i_op, i_a, i_b, o_c, o_f, o_valid, |
o_illegal, o_busy); |
input i_clk, i_rst, i_ce; |
input [3:0] i_op; |
input [31:0] i_a, i_b; |
input i_valid; |
output reg [31:0] o_c; |
output wire [3:0] o_f; |
output wire o_valid; |
output wire o_illegal; |
output wire o_busy; |
|
// Rotate-left logic |
wire [63:0] w_rol_tmp; |
assign w_rol_tmp = { i_a, i_a } << i_b[4:0]; |
reg [31:0] r_rol_result; |
always @(posedge i_clk) |
r_rol_result <= w_rol_tmp[63:32]; // Won't set flags |
|
// Shift register logic |
reg [32:0] r_lsr_result, r_asr_result, r_lsl_result; |
always @(posedge i_clk) |
begin |
r_asr_result <= (|i_b[31:5])? {(33){i_a[31]}} |
: ( $signed({i_a, 1'b0 })>>> (i_b[4:0]) );// ASR |
r_lsr_result <= (|i_b[31:5])? 33'h00 |
: ( { i_a, 1'b0 } >> (i_b[4:0]) );// LSR |
r_lsl_result <= (|i_b[31:5])? 33'h00 : {1'b0, i_a } << i_b[4:0]; // LSL |
end |
|
// Bit reversal pre-logic |
wire [31:0] w_brev_result; |
reg [31:0] r_brev_result; |
genvar k; |
generate |
for(k=0; k<32; k=k+1) |
begin : bit_reversal_cpuop |
assign w_brev_result[k] = i_b[31-k]; |
end endgenerate |
always @(posedge i_clk) |
r_brev_result <= w_brev_result; |
|
// Popcount logic |
wire [31:0] w_popc_result; |
reg [5:0] r_popc_result; |
always @(posedge i_clk) |
r_popc_result = |
({5'h0,i_b[ 0]}+{5'h0,i_b[ 1]}+{5'h0,i_b[ 2]}+{5'h0,i_b[ 3]}) |
+({5'h0,i_b[ 4]}+{5'h0,i_b[ 5]}+{5'h0,i_b[ 6]}+{5'h0,i_b[ 7]}) |
+({5'h0,i_b[ 8]}+{5'h0,i_b[ 9]}+{5'h0,i_b[10]}+{5'h0,i_b[11]}) |
+({5'h0,i_b[12]}+{5'h0,i_b[13]}+{5'h0,i_b[14]}+{5'h0,i_b[15]}) |
+({5'h0,i_b[16]}+{5'h0,i_b[17]}+{5'h0,i_b[18]}+{5'h0,i_b[19]}) |
+({5'h0,i_b[20]}+{5'h0,i_b[21]}+{5'h0,i_b[22]}+{5'h0,i_b[23]}) |
+({5'h0,i_b[24]}+{5'h0,i_b[25]}+{5'h0,i_b[26]}+{5'h0,i_b[27]}) |
+({5'h0,i_b[28]}+{5'h0,i_b[29]}+{5'h0,i_b[30]}+{5'h0,i_b[31]}); |
assign w_popc_result = { 26'h00, r_popc_result }; |
|
// Prelogic for our flags registers |
wire z, n, v; |
reg c, pre_sign, set_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 |
|
reg [31:0] r_logical; |
always @(posedge i_clk) |
r_logical <= (i_op[0]) ? (i_a & i_b) : (i_a | i_b); |
|
reg [32:0] r_sum, r_diff; |
reg [31:0] r_ldilo, r_bypass, r_xor; |
always @(posedge i_clk) |
r_sum <= i_a + i_b; // Add |
always @(posedge i_clk) |
r_diff <= {1'b0, i_a } - { 1'b0, i_b }; // SUB |
always @(posedge i_clk) |
r_xor <= i_a ^ i_b; // XOR |
always @(posedge i_clk) |
r_ldilo <= { i_a[31:16], i_b[15:0] }; // LDILO |
always @(posedge i_clk) |
r_bypass <= i_b; // LOD/MOV,ETC |
|
reg mpyhi; |
wire mpybusy; |
|
// |
// Multiply logic |
// |
reg [63:0] r_mpy_result; // Our final goal |
|
// The three clock option |
reg [31:0] r_mpy_a_input, r_mpy_b_input; |
reg r_mpy_signed; |
reg [1:0] mpypipe; |
|
wire mpy; |
assign mpy = (i_op[3:1] == 3'h5)||(i_op[3:0] != 4'h8); |
|
// First clock, latch in the inputs |
always @(posedge i_clk) |
begin |
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]; |
|
mpyhi = i_op[1]; |
end |
|
// 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_o, pp_i, pp_l; // F, O, I and L 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_o<=r_mpy_a_input[31:16]*r_mpy_b_input[15: 0]; |
pp_i<=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 |
// r_mpy_result[63:16] <= |
// { 32'h00, pp_l[31:16] } |
// + { 16'h00, pp_o } |
// + { 16'h00, pp_i } |
// + { pp_s, 15'h00 } |
// + { pp_f, 16'h00 }; |
// |
// 16'h00 16'h00 pp_l[31:16] ppl[15:] |
// 16'h00 pp_o[31:16] pp_o[15:0] 16'h00 |
// 16'h00 pp_i[31:16] pp_i[15:0] 16'h00 |
// pp_s[32:17] pp_s[16:1] pp_s[0],15'h0 16'h00 |
// pp_f[31:16] pp_f[31:16] 16'h00 16'h00 |
// |
// 16'h0 15'h0,lo[32] lo[31:16] lo[15:] |
// 15'h0,oi[32] oi[31:16] oi[15:0] 16'h00 |
// hi[31:0] hi[15:0] 16'h00 |
// |
// |
reg [32:0] partial_mpy_oi, partial_mpy_lo; |
reg [31:0] partial_mpy_hi; |
always @(posedge i_clk) |
begin |
partial_mpy_lo[30:0]<= pp_l[30:0]; |
partial_mpy_lo[32:31]<= pp_s[0]+pp_l[31]; |
partial_mpy_oi[32:0]<= pp_o + pp_i; |
partial_mpy_hi[31:0]<= pp_s[32:1] + pp_f; |
end |
reg partial_mpy_2cl, partial_mpy_2ch; |
reg [31:0] partial_mpy_2lo, partial_mpy_2hi; |
// Fourth clock -- Finish adding our partial results |
always @(posedge i_clk) |
begin |
partial_mpy_2lo[15:0] <= partial_mpy_lo[15:0]; |
{ partial_mpy_2cl, partial_mpy_2lo[31:16] } |
<= partial_mpy_oi[15:0] + partial_mpy_lo[31:16]; |
{ partial_mpy_2ch, partial_mpy_2hi[15:0] } |
<= partial_mpy_oi[32:16] + partial_mpy_hi[16:0]; |
partial_mpy_2hi[31:17] <= partial_mpy_2hi[31:17]; |
end |
// Fifth clock -- deal with final carries |
always @(posedge i_clk) |
begin |
r_mpy_result[31:0] <= partial_mpy_2lo[31:0]; |
r_mpy_result[63:32] <= partial_mpy_2hi+ |
{ 14'h0,partial_mpy_2ch,15'h0, partial_mpy_2cl}; |
end |
// Fifth clock -- results are available for writeback. |
|
// |
// The master ALU case statement |
// |
reg [3:0] r_op; |
always @(posedge i_clk) |
begin |
r_op <= i_op; |
pre_sign <= (i_a[31]); |
c <= 1'b0; |
casez(r_op) |
4'b0000:{c,o_c } <= r_diff; // CMP/SUB |
4'b00?1: o_c <= r_logical; // BTST/And/Or |
4'b0010:{c,o_c } <= r_sum; // Add |
4'b0100: o_c <= r_xor; // Xor |
4'b0101:{o_c,c } <= r_lsr_result; // LSR |
4'b0110:{c,o_c } <= r_lsl_result; // LSL |
4'b0111:{o_c,c } <= r_asr_result; // ASR |
4'b1000: o_c <= r_mpy_result[31:0]; // MPY |
4'b1001: o_c <= r_ldilo; // LODILO |
4'b1010: o_c <= r_mpy_result[63:32]; // MPYHU |
4'b1011: o_c <= r_mpy_result[63:32]; // MPYHS |
4'b1100: o_c <= r_brev_result; // BREV |
4'b1101: o_c <= w_popc_result; // POPC |
4'b1110: o_c <= r_rol_result; // ROL |
default: o_c <= r_bypass; // MOV, LDI |
endcase |
end |
|
// With the multiply implemented (as above), there are no illegal |
// results. |
assign o_illegal = 1'b0; |
|
assign z = (o_c == 32'h0000); // This really costs us a clock ... |
assign n = (o_c[31]); |
assign v = (set_ovfl)&&(pre_sign != o_c[31]); |
|
assign o_f = { v, n, c, z }; |
|
reg [2:0] alu_pipe; |
always @(posedge i_clk) |
if (i_rst) |
alu_pipe <= 3'h0; |
else |
alu_pipe <= { alu_pipe[1], (i_ce)&(~mpy)|alu_pipe[0], |
(i_ce)&(mpy) }; |
// |
// A longer pipeline would look like: |
// |
// alu_pipe <= { alu_pipe[2:1], (i_ce)&(~mpy)|alu_pipe[1], alu_pipe[0], |
// (i_ce)&mpy; |
// o_busy <= (|alu_pipe[1:0]) |
|
assign o_valid = alu_pipe[2]; |
assign o_busy = alu_pipe[0]; |
endmodule |
/rtl/cpu/wbarbiter.v
0,0 → 1,185
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbarbiter.v |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// Purpose: At some point in time, I might wish to have two masters connect |
// to the same wishbone bus. As an example, I might wish to have |
// both the instruction fetch and the load/store operators |
// of my Zip CPU access the the same bus. How shall they both |
// get access to the same resource? This module allows the |
// wishbone interfaces from two sources to drive the bus, while |
// guaranteeing that only one drives the bus at a time. |
// |
// The core logic works like this: |
// |
// 1. If 'A' or 'B' asserts the o_cyc line, a bus cycle will begin, |
// with acccess granted to whomever requested it. |
// 2. If both 'A' and 'B' assert o_cyc at the same time, only 'A' |
// will be granted the bus. (If the alternating parameter |
// is set, A and B will alternate who gets the bus in |
// this case.) |
// 3. The bus will remain owned by whomever the bus was granted to |
// until they deassert the o_cyc line. |
// 4. At the end of a bus cycle, o_cyc is guaranteed to be |
// deasserted (low) for one clock. |
// 5. On the next clock, bus arbitration takes place again. If |
// 'A' requests the bus, no matter how long 'B' was |
// waiting, 'A' will then be granted the bus. (Unless |
// again the alternating parameter is set, then the |
// access is guaranteed to switch to B.) |
// |
// |
// 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. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// `define WBA_ALTERNATING |
module wbarbiter(i_clk, i_rst, |
// Bus A -- gets priority when not alternating |
i_a_adr, i_a_dat, i_a_we, i_a_stb, i_a_cyc, o_a_ack, o_a_stall, o_a_err, |
// Bus B |
i_b_adr, i_b_dat, i_b_we, i_b_stb, i_b_cyc, o_b_ack, o_b_stall, o_b_err, |
// Both buses |
o_adr, o_dat, o_we, o_stb, o_cyc, i_ack, i_stall, i_err); |
// 18 bits will address one GB, 4 bytes at a time. |
// 19 bits will allow the ability to address things other than just |
// the 1GB of memory we are expecting. |
parameter DW=32, AW=19; |
// Wishbone doesn't use an i_ce signal. While it could, they dislike |
// what it would (might) do to the synchronous reset signal, i_rst. |
input i_clk, i_rst; |
input [(AW-1):0] i_a_adr, i_b_adr; |
input [(DW-1):0] i_a_dat, i_b_dat; |
input i_a_we, i_a_stb, i_a_cyc; |
input i_b_we, i_b_stb, i_b_cyc; |
output wire o_a_ack, o_b_ack, o_a_stall, o_b_stall, |
o_a_err, o_b_err; |
output wire [(AW-1):0] o_adr; |
output wire [(DW-1):0] o_dat; |
output wire o_we, o_stb, o_cyc; |
input i_ack, i_stall, i_err; |
|
// All the fancy stuff here is done with the three primary signals: |
// o_cyc |
// w_a_owner |
// w_b_owner |
// These signals are helped by r_cyc, r_a_owner, and r_b_owner. |
// If you understand these signals, all else will fall into place. |
|
// r_cyc just keeps track of the last o_cyc value. That way, on |
// the next clock we can tell if we've had one non-cycle before |
// starting another cycle. Specifically, no new cycles will be |
// allowed to begin unless r_cyc=0. |
reg r_cyc; |
always @(posedge i_clk) |
if (i_rst) |
r_cyc <= 1'b0; |
else |
r_cyc <= o_cyc; |
|
// 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) |
assign o_cyc = ((~r_cyc)&&((i_a_cyc)||(i_b_cyc))) || ((r_cyc)&&((w_a_owner)||(w_b_owner))); |
|
|
// Register keeping track of the last owner, wire keeping track of the |
// current owner allowing us to not lose a clock in arbitrating the |
// first clock of the bus cycle |
reg r_a_owner, r_b_owner; |
wire w_a_owner, w_b_owner; |
`ifdef WBA_ALTERNATING |
reg r_a_last_owner; |
|
`endif |
always @(posedge i_clk) |
if (i_rst) |
begin |
r_a_owner <= 1'b0; |
r_b_owner <= 1'b0; |
end else begin |
r_a_owner <= w_a_owner; |
r_b_owner <= w_b_owner; |
`ifdef WBA_ALTERNATING |
if (w_a_owner) |
r_a_last_owner <= 1'b1; |
else if (w_b_owner) |
r_a_last_owner <= 1'b0; |
`endif |
end |
// |
// If you are the owner, retain ownership until i_x_cyc is no |
// longer asserted. Likewise, you cannot become owner until o_cyc |
// is de-asserted for one cycle. |
// |
// 'A' is given arbitrary priority over 'B' |
// 'A' may own the bus only if he wants it. When 'A' drops i_a_cyc, |
// o_cyc must drop and so must w_a_owner on the same cycle. |
// However, when 'A' asserts i_a_cyc, he can only capture the bus if |
// it's had an idle cycle. |
// The same is true for 'B' with one exception: if both contend for the |
// bus on the same cycle, 'A' arbitrarily wins. |
`ifdef WBA_ALTERNATING |
assign w_a_owner = (i_a_cyc) // if A requests ownership, and either |
&& ((r_a_owner) // A has already been recognized or |
|| ((~r_cyc) // the bus is free and |
&&((~i_b_cyc) // B has not requested, or if he |
||(~r_a_last_owner)) )); // has, it's A's turn |
assign w_b_owner = (i_b_cyc)&& ((r_b_owner) || ((~r_cyc)&&((~i_a_cyc)||(r_a_last_owner)) )); |
`else |
assign w_a_owner = (i_a_cyc)&& ((r_a_owner) || (~r_cyc) ); |
assign w_b_owner = (i_b_cyc)&& ((r_b_owner) || ((~r_cyc)&&(~i_a_cyc)) ); |
`endif |
|
// 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_adr = (w_a_owner) ? i_a_adr : i_b_adr; |
assign o_dat = (w_a_owner) ? i_a_dat : i_b_dat; |
assign o_we = (w_a_owner) ? i_a_we : i_b_we; |
assign o_stb = (o_cyc) && ((w_a_owner) ? i_a_stb : i_b_stb); |
|
// 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 = (w_a_owner) ? i_ack : 1'b0; |
assign o_b_ack = (w_b_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 = (w_a_owner) ? i_stall : 1'b1; |
assign o_b_stall = (w_b_owner) ? i_stall : 1'b1; |
|
// |
// |
assign o_a_err = (w_a_owner) ? i_err : 1'b0; |
assign o_b_err = (w_b_owner) ? i_err : 1'b0; |
|
endmodule |
|
/rtl/cpu/fastcache.v
0,0 → 1,277
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: fastcache.v |
// |
// 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). |
// |
// 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. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
module fastcache(i_clk, i_rst, i_new_pc, i_clear_cache, |
// i_early_branch, i_from_addr, |
i_stall_n, i_pc, o_i, o_pc, o_v, |
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, |
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data, |
o_illegal); |
parameter LGCACHELEN = 8, ADDRESS_WIDTH=24, |
CACHELEN=(1<<LGCACHELEN), BUSW=32, AW=ADDRESS_WIDTH, |
CW=LGCACHELEN, PW=LGCACHELEN-5; |
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; |
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; |
// |
output reg o_illegal; |
|
// Fixed bus outputs: we read from the bus only, never write. |
// Thus the output data is ... irrelevant and don't care. We set it |
// to zero just to set it to something. |
assign o_wb_we = 1'b0; |
assign o_wb_data = 0; |
|
wire r_v; |
reg [(BUSW-1):0] cache [0:((1<<CW)-1)]; |
reg [(AW-CW-1):0] tags [0:((1<<(CW-PW))-1)]; |
reg [((1<<(CW-PW))-1):0] vmask; |
|
reg [(AW-1):0] lastpc; |
reg [(CW-1):0] rdaddr; |
reg [(AW-1):CW] tagvalipc, tagvallst; |
wire [(AW-1):CW] tagval; |
wire [(AW-1):PW] lasttag; |
reg illegal_valid; |
reg [(AW-1):PW] illegal_cache; |
|
// initial o_i = 32'h76_00_00_00; // A NOOP instruction |
// initial o_pc = 0; |
reg [(BUSW-1):0] r_pc_cache, r_last_cache; |
reg [(AW-1):0] r_pc, r_lastpc; |
reg isrc; |
always @(posedge i_clk) |
if (~r_v) |
isrc <= 1'b0; |
else if ((i_stall_n)||(i_new_pc)) |
isrc <= 1'b1; |
always @(posedge i_clk) |
r_pc_cache <= cache[i_pc[(CW-1):0]]; |
always @(posedge i_clk) |
r_last_cache <= cache[lastpc[(CW-1):0]]; |
always @(posedge i_clk) |
r_pc <= i_pc; |
always @(posedge i_clk) |
r_lastpc <= lastpc; |
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; |
initial tagvalipc = 0; |
always @(posedge i_clk) |
tagvalipc <= tags[i_pc[(CW-1):PW]]; |
initial tagvallst = 0; |
always @(posedge i_clk) |
tagvallst <= tags[lastpc[(CW-1):PW]]; |
assign tagval = (tagsrc)?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 |
// isn't valid, but the decoder is stalled, get the next address |
// anyway. |
initial lastpc = 0; |
always @(posedge i_clk) |
if (((r_v)&&(i_stall_n))||(i_clear_cache)||(i_new_pc)) |
lastpc <= i_pc; |
|
assign lasttag = lastpc[(AW-1):PW]; |
// initial lasttag = 0; |
// always @(posedge i_clk) |
// if (((r_v)&&(i_stall_n))||(i_clear_cache)||(i_new_pc)) |
// lasttag <= i_pc[(AW-1):PW]; |
|
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)&& |
(tagvallst == lastpc[(AW-1):CW]) |
&&(vmask[lastpc[(CW-1):PW]])); |
|
reg [1:0] delay; |
|
initial delay = 2'h3; |
reg rvsrc; |
always @(posedge i_clk) |
if ((i_rst)||(i_clear_cache)||(i_new_pc)||((r_v)&&(i_stall_n))) |
begin |
// r_v <= r_v_from_pc; |
rvsrc <= 1'b1; |
delay <= 2'h2; |
end else if (~r_v) begin // Otherwise, r_v was true and we were |
// stalled, hence only if ~r_v |
rvsrc <= 1'b0; |
if (o_wb_cyc) |
delay <= 2'h2; |
else if (delay != 0) |
delay <= delay + 2'b11; // i.e. delay -= 1; |
end |
reg r_v_from_pc, r_v_from_last; |
always @(posedge i_clk) |
r_v_from_pc <= w_v_from_pc; |
always @(posedge i_clk) |
r_v_from_last <= w_v_from_last; |
|
assign r_v = ((rvsrc)?(r_v_from_pc):(r_v_from_last)); |
assign o_v = ((rvsrc)?(r_v_from_pc):(r_v_from_last))&&(~i_new_pc); |
|
reg last_ack; |
initial last_ack <= 1'b0; |
always @(posedge i_clk) |
last_ack <= (o_wb_cyc)&&( |
(rdaddr[(PW-1):1]=={(PW){1'b1}}) |
&&((rdaddr[0])||(i_wb_ack))); |
|
reg needload; |
initial needload = 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))); |
|
reg last_addr; |
initial last_addr = 1'b0; |
always @(posedge i_clk) |
last_addr <= (o_wb_cyc)&&(o_wb_addr[(PW-2):1] == {(PW-1){1'b1}}) |
&&((~i_wb_stall)|(o_wb_addr[0])); |
|
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; |
o_wb_stb <= 1'b0; |
end else if (o_wb_cyc) |
begin |
if (i_wb_err) |
o_wb_stb <= 1'b0; |
else if ((o_wb_stb)&&(~i_wb_stall)&&(last_addr)) |
o_wb_stb <= 1'b0; |
|
if (((i_wb_ack)&&(last_ack))||(i_wb_err)) |
o_wb_cyc <= 1'b0; |
|
// else if (rdaddr[(PW-1):1] == {(PW-1){1'b1}}) |
// tags[lastpc[(CW-1):PW]] <= lastpc[(AW-1):CW]; |
|
end else if (needload) |
begin |
o_wb_cyc <= 1'b1; |
o_wb_stb <= 1'b1; |
end |
|
always @(posedge i_clk) |
if (o_wb_cyc) // &&(i_wb_ack) |
tags[o_wb_addr[(CW-1):PW]] <= o_wb_addr[(AW-1):CW]; |
always @(posedge i_clk) |
if ((o_wb_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}} }; |
|
// Can't initialize an array, so leave cache uninitialized |
// We'll also never get an ack without sys being active, so skip |
// that check. Or rather, let's just use o_wb_cyc instead. This |
// will work because multiple writes to the same address, ending with |
// a valid write, aren't a problem. |
always @(posedge i_clk) |
if (o_wb_cyc) // &&(i_wb_ack) |
cache[rdaddr] <= i_wb_data; |
|
// VMask ... is a section loaded? |
initial vmask = 0; |
always @(posedge i_clk) |
if ((i_rst)||(i_clear_cache)) |
vmask <= 0; |
else begin |
if ((o_wb_cyc)&&(i_wb_ack)&&(last_ack)) |
vmask[rdaddr[(CW-1):PW]] <= 1'b1; |
if ((~o_wb_cyc)&&(needload)) |
vmask[lastpc[(CW-1):PW]] <= 1'b0; |
end |
|
initial illegal_cache = 0; |
initial illegal_valid = 0; |
always @(posedge i_clk) |
if ((i_rst)||(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):PW]; |
illegal_valid <= 1'b1; |
end |
|
initial o_illegal = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(i_clear_cache)) |
o_illegal <= 1'b0; |
else |
o_illegal <= (illegal_valid) |
&&(illegal_cache == i_pc[(AW-1):PW]); |
|
endmodule |
/rtl/cpu/ifastdec.v
0,0 → 1,620
/////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: ifastdec.v |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// Purpose: This RTL file specifies how instructions are to be decoded |
// into their underlying meanings. It is different from the |
// standard idecode.v file in that this one takes two clocks, and is |
// pipelined. This one is designed for a 5ns clock cycle, hence it is |
// a "fast" decoder--even though the old decoder took one cycle in 10ns. |
// From that standpoint, the two may well be ... comparable in speed. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////////// |
// |
// |
// |
`define CPU_CC_REG 4'he |
`define CPU_PC_REG 4'hf |
// |
`include "cpudefs.v" |
// |
// |
// |
module ifastdec(i_clk, i_rst, i_ce, i_stalled, |
i_instruction, i_gie, i_pc, i_pf_valid, |
i_illegal, |
o_phase, o_illegal, |
o_pc, o_gie, |
o_R, o_A, o_B, 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 |
); |
parameter ADDRESS_WIDTH=24, IMPLEMENT_MPY=1, EARLY_BRANCHING=1, |
IMPLEMENT_DIVIDE=1, IMPLEMENT_FPU=0, AW = ADDRESS_WIDTH; |
input i_clk, i_rst, i_ce, i_stalled; |
input [31:0] i_instruction; |
input i_gie; |
input [(AW-1):0] i_pc; |
input i_pf_valid, i_illegal; |
output reg o_phase; |
output reg o_illegal; |
output reg [(AW-1):0] o_pc; |
output reg o_gie; |
output reg [6:0] o_R, o_A, o_B; |
output reg [31:0] o_I; |
output reg o_zI; |
output reg [3:0] o_cond; |
output reg o_wF; |
output reg [3:0] o_op; |
output reg o_ALU, o_M, o_DV, o_FP, o_break; |
output reg o_lock; |
output reg o_wR, o_rA, o_rB; |
output reg o_early_branch; |
output reg [(AW-1):0] o_branch_pc; |
output reg o_ljmp; |
output reg o_pipe; |
|
|
////// |
// |
// Path 1: Full size instruction |
// |
// Prefix: wf (wire, full) |
// |
////// |
|
// The 5-bit opcode, as extracted |
wire [4:0] wf_op; |
// Instruction types |
wire wf_ldi, wf_mov, wf_cmptst, wf_ldilo, wf_brev, wf_noop, |
wf_break, wf_lock, |
wf_ljmp, wf_ALU, wf_MEM, wf_DIV, wf_FPU; |
wire [4:0] wf_R, wf_A, wf_B; // Instruction registers |
wire [3:0] wf_cond; |
wire wf_wF; // Write flags? |
wire wf_wR_n, // Write destination register? or not? |
wf_rA, // Read register A? |
wf_rB; // Read register B? |
wire [22:0] wf_pI; // Partial immediate ... |
wire wf_Iz; |
|
assign wf_op = i_instruction[26:22]; |
assign wf_brev = (wf_op == 5'hc); |
assign wf_mov = (wf_op == 5'h0f); |
assign wf_ldi = (wf_op[4:1] == 4'hb); |
assign wf_cmptst=(wf_op[4:1] == 4'h8); |
assign wf_ldilo= (wf_op[4:0] == 5'h9); |
assign wf_noop = (wf_op[4:0] == 5'h18)&&(wf_R[3:1] == 3'h7); |
assign wf_break= (wf_op[4:0] == 5'h19)&&(wf_R[3:1] == 3'h7); |
assign wf_lock = (wf_op[4:0] == 5'h1a)&&(wf_R[3:1] == 3'h7); |
// |
assign wf_R = { (wf_mov)&&(i_gie)?i_instruction[18]:i_gie, (i_instruction[30:27]) }; |
assign wf_A = { (wf_mov)&&(i_gie)?i_instruction[18]:i_gie, (i_instruction[30:27]) }; |
assign wf_B = { (wf_mov)&&(i_gie)?i_instruction[13]:i_gie, (i_instruction[17:14]) }; |
// |
assign wf_ALU = (~wf_op[4]); |
assign wf_MEM = ( wf_op[4:1] == 4'h9); |
assign wf_DIV = ( wf_op[4:1] == 4'ha); |
assign wf_FPU = ( wf_op[4:2] == 3'h7)&&(i_instruction[30:28]==3'h7); |
assign wf_ljmp = (i_instruction == 32'h7c87c000); |
|
assign wf_pI = (wf_ldi) ? { i_instruction[22:0] } // LDI |
:((wf_mov) ?{ {(23-13){i_instruction[12]}}, i_instruction[12:0] } // Move |
:((~i_instruction[18]) ? { {(23-18){i_instruction[17]}}, i_instruction[17:0] } |
: { {(23-14){i_instruction[13]}}, i_instruction[13:0] } |
)); |
assign wf_Iz= (wf_pI == 23'h00); |
|
// Will we be writing register R? |
assign wf_wR_n = ((wf_MEM)&&(wf_op[0])) // Store's dont write regs |
// Neither do NOOP, BREAK, or LOCKs |
||((wf_op[4:3]==2'b11)&&(wf_R[3:1]==3'h7)) |
// nor CMPs and TSTs |
||(wf_cmptst); |
// Do we read register 'A'? |
assign wf_rA = (wf_FPU)&&(wf_op[4:1]!=4'he) // FPU, but not CVT or INT |
||(wf_DIV) |
||(wf_ALU)&&(~wf_mov) |
||(wf_MEM)&&(wf_op[0]) |
||(wf_cmptst); |
// Do we need to read register 'B'? |
assign wf_rB = (wf_mov)||((i_instruction[18])&&(~wf_ldi)); |
|
// What are the conditions for this instruction? NOOP, BREAK, and LOCK |
// are also unconditional, but they'll just ignore this setting |
assign wf_cond = (wf_ldi) ? 4'h8 // LDI is unconditional |
: { (i_instruction[21:19]==3'h0), i_instruction[21:19] }; |
// How about the flags, will we be writing them? |
assign wf_wF = (wf_cmptst) // Compares always write flags |
||((wf_cond[3])&&( |
// FPU and DIV instructions always write flags |
(wf_FPU)||(wf_DIV) |
// So do ALU instructions, UNLESS the ALU |
// instruction is a MOV, LDILO, or BREV, or |
// the results are being written into the PC |
// or CC register--those don't set flags |
||((wf_ALU)&&(~wf_mov)&&(~wf_ldilo)&&(~wf_brev) |
&&(i_instruction[30:28] != 3'h7)))); |
|
reg [3:0] rf_op; |
reg rf_break, rf_lock; |
reg [4:0] rf_R, rf_A, rf_B; |
reg rf_ALU, rf_MEM, rf_DIV, rf_FPU, rf_ljmp; |
reg [3:0] rf_cond; |
reg rf_rA, rf_rB, rf_wR, rf_wF; |
|
reg [22:0] rf_pI; |
reg rf_Iz; |
wire [31:0] wf_I; |
assign wf_I = { {(32-22){rf_pI[22]}}, rf_pI[21:0] }; |
|
reg rf_early_branch; |
reg [(AW-1):0] rf_branch_pc; |
always @(posedge i_clk) |
if (i_ce) |
begin |
rf_op <= wf_op[3:0]; |
rf_break<= wf_break; |
rf_lock <= wf_lock; |
// |
rf_R <= (wf_R); |
rf_A <= (wf_A); |
rf_B <= (wf_B); |
// |
rf_ALU <= (~wf_op[4]); |
rf_MEM <= ( wf_op[4:1] == 4'h9); |
rf_DIV <= ( wf_op[4:1] == 4'ha); |
rf_FPU <= ( wf_op[4:2] == 3'h7)&&(i_instruction[30:28]==3'h7); |
rf_ljmp <= (i_instruction == 32'h7c87c000); |
|
rf_pI <= wf_pI; |
rf_Iz<= wf_Iz; |
|
// What are the conditions of this instruction? |
rf_cond <= wf_cond; |
|
// Do we read register 'A'? |
rf_rA <= wf_rA; |
// Do we need to read register 'B'? |
rf_rB <= wf_rB; |
// Will we be writing register 'R'? |
rf_wR <= ~wf_wR_n; |
// How about the flags, will we be writing those? |
rf_wF <= wf_wF; |
|
// |
rf_early_branch <= |
// PC is the result |
(wf_R[3:0]==4'hf) |
// Unconditional instruction |
&&(i_instruction[21:19]==3'h0) |
&& |
// Either an ADD #x,PC |
((wf_op == 5'h02)&&(~i_instruction[18]) |
// Or a LOD #x,PC |
||(wf_ldi)); |
rf_branch_pc <= (wf_ldi)?{{(AW-22){wf_pI[22]}},wf_pI[21:0]} |
:(i_pc + {{(AW-18){i_instruction[17]}}, |
i_instruction[16:0]}); |
end |
|
////// |
// |
// Path 2: Half size instruction, high half |
// |
// Prefix: wh (wire, high-half) |
// |
////// |
|
// The 5-bit opcode, as extracted -- same as wf_ |
// Instruction types -- same as wf_ |
wire [4:0] wh_R, wh_A, wh_B; // Instruction registers |
wire [3:0] wh_cond; |
wire wh_wF; // Write flags? |
wire wh_wR, wh_wR_n, // Write destination register? or not? |
wh_rA, // Read register A? |
wh_rB; // Read register B? |
wire [4:0] wh_pI; // Partial immediate ... |
wire wh_Iz; |
|
assign wh_R = { i_gie, (i_instruction[30:27]) }; |
assign wh_A = { i_gie, (i_instruction[30:27]) }; |
assign wh_B = { i_gie, (i_instruction[17:14]) }; |
// |
|
assign wh_pI = (wf_ldi) ? { i_instruction[18:14] } // LDI |
:((~i_instruction[18]) |
? { i_instruction[17], i_instruction[17:14] } |
: 5'h0); |
assign wh_Iz= (wh_pI == 5'h0); |
|
// Will we be writing register R? |
assign wh_wR_n = ((wf_MEM)&&(wf_op[0])) // Store's dont write regs |
// Neither do NOOP, BREAK, or LOCKs |
||((wf_op[4:3]==2'b11)&&(wh_R[3:1]==3'h7)) |
// nor CMPs and TSTs |
||(wf_cmptst); |
|
assign wh_cond = (wf_ldi) ? 4'h8 // LDI is unconditional |
: { (i_instruction[20:19]==2'h0), 1'b0, i_instruction[20:19] }; |
// How about the flags, will we be writing them? |
assign wh_wF = (wf_cmptst) // Compares always write flags |
||((wh_cond[3])&&( |
// FPU and DIV instructions always write flags |
(wf_FPU)||(wf_DIV) |
// So do ALU instructions, UNLESS the ALU |
// instruction is a MOV, LDILO, or BREV, or |
// the results are being written into the PC |
// or CC register--those don't set flags |
||((wf_ALU)&&(~wf_mov)&&(~wf_ldilo)&&(~wf_brev) |
&&(i_instruction[30:28] != 3'h7)))); |
|
reg [3:0] rh_op; |
reg rh_break, rh_lock; |
reg [4:0] rh_R, rh_A, rh_B; |
reg rh_ALU, rh_MEM, rh_DIV, rh_FPU; |
reg [3:0] rh_cond; |
reg rh_rA, rh_rB, rh_wR, rh_wF; |
wire [31:0] wh_I; |
reg [4:0] rh_pI; |
reg rh_Iz; |
assign wh_I = { {(32-4){rh_pI[4]}}, rh_pI[3:0] }; |
always @(posedge i_clk) |
if (i_ce) |
begin |
rh_op <= wf_op[3:0]; |
rh_break<= wf_break; |
rh_lock <= wf_lock; |
// |
rh_R <= (wh_R); |
rh_A <= (wh_A); |
rh_B <= (wh_B); |
// |
rh_ALU <= wf_ALU; |
rh_MEM <= wf_MEM; |
rh_DIV <= wf_DIV; |
rh_FPU <= wf_FPU; |
|
rh_pI <= wh_pI; |
rh_Iz <= wh_Iz; |
|
// What are the conditions of this instruction? |
rh_cond <= wh_cond; |
|
// Do we read register 'A'? |
rh_rA <= (wf_FPU)&&(wf_op[4:1]!=4'he) |
||(wf_DIV) |
||(wf_ALU)&&(~wf_mov) |
||(wf_MEM)&&(wf_op[0]) |
||(wf_cmptst); |
// Do we need to read register 'B'? |
rh_rB <= (i_instruction[18])&&(~wf_ldi); |
// Will we be writing register 'R'? |
rh_wR <= ~wh_wR_n; |
rh_wF <= wh_wF; |
end |
|
|
////// |
// |
// Path 3: Half size instruction, low half |
// |
////// |
|
// The 5-bit opcode, as extracted |
wire [4:0] wl_op; |
// Instruction types |
wire wl_ldi, wl_mov, wl_cmptst, wl_ldilo, wl_brev, wl_noop, |
wl_break, wl_lock, |
wl_ljmp, wl_ALU, wl_MEM, wl_DIV, wl_FPU; |
wire [4:0] wl_R, wl_A, wl_B; // Instruction registers |
wire [3:0] wl_cond; |
wire wl_wF; // Write flags? |
wire wl_wR, wl_wR_n, // Write destination register? or not? |
wl_rA, // Read register A? |
wl_rB; // Read register B? |
wire [4:0] wl_pI; // Partial immediate ... |
wire wl_Iz; |
|
assign wl_op = i_instruction[9:5]; |
assign wl_brev = (wl_op == 5'hc); |
assign wl_mov = (wl_op == 5'h0f); |
assign wl_ldi = (wl_op[4:1] == 4'hb); |
assign wl_cmptst=(wl_op[4:1] == 4'h8); |
assign wl_ldilo= (wl_op[4:0] == 5'h9); |
assign wl_noop = (wl_op[4:0] == 5'h18)&&(wl_R[3:1] == 3'h7); |
assign wl_break= (wl_op[4:0] == 5'h19)&&(wl_R[3:1] == 3'h7); |
assign wl_lock = (wl_op[4:0] == 5'h1a)&&(wl_R[3:1] == 3'h7); |
// |
assign wl_R = { i_gie, i_instruction[13:10] }; |
assign wl_A = { i_gie, i_instruction[13:10] }; |
assign wl_B = { i_gie, i_instruction[ 3: 0] }; |
// |
assign wl_ALU = (~wl_op[4]); |
assign wl_MEM = ( wl_op[4:1] == 4'h9); |
assign wl_DIV = ( wl_op[4:1] == 4'ha); |
assign wl_FPU = ( wl_op[4:2] == 3'h7)&&(i_instruction[30:28]==3'h7); |
assign wl_ljmp = ( i_instruction[21:19] == 3'h00) // 1111_10010_1_1111 |
&&(i_instruction[13:0]==14'h3e5f); |
|
assign wl_pI = (wl_ldi) ? { i_instruction[4:0] } // LDI |
:((~i_instruction[4]) |
? { i_instruction[3], i_instruction[3:0] } |
: 5'h0); |
assign wl_Iz= (wl_pI == 5'h0); |
|
// Will we be writing register R? |
assign wl_wR_n = ((wl_MEM)&&(wl_op[0])) // Store's dont write regs |
// Neither do NOOP, BREAK, or LOCKs |
||((wl_op[4:3]==2'b11)&&(wl_R[3:1]==3'h7)) |
// nor CMPs and TSTs |
||(wl_cmptst); |
|
// What are the conditions for this instruction? NOOP, BREAK, and LOCK |
// are also unconditional, but they'll just ignore this setting |
assign wl_cond = (wl_ldi) ? 4'h8 // LDI is unconditional |
: { (i_instruction[20:19]==2'h0), |
1'b0, i_instruction[20:19] }; |
// How about the flags, will we be writing them? |
assign wl_wF = (wl_cmptst) // Compares always write flags |
||((wl_cond[3])&&( |
// FPU and DIV instructions always write flags |
(wl_FPU)||(wl_DIV) |
// So do ALU instructions, UNLESS the ALU |
// instruction is a MOV, LDILO, or BREV, or |
// the results are being written into the PC |
// or CC register--those don't set flags |
||((wl_ALU)&&(~wl_mov)&&(~wl_ldilo)&&(~wl_brev) |
&&(i_instruction[13:11] != 3'h7)))); |
|
reg [3:0] rl_op; |
reg rl_break, rl_lock; |
reg [4:0] rl_R, rl_A, rl_B; |
reg rl_ALU, rl_MEM, rl_DIV, rl_FPU, rl_ljmp; |
reg [3:0] rl_cond; |
reg rl_rA, rl_rB, rl_wR, rl_wF; |
wire [31:0] wl_I; |
reg [4:0] rl_pI; |
reg rl_Iz; |
assign wl_I = { {(32-4){rl_pI[4]}}, rl_pI[3:0] }; |
always @(posedge i_clk) |
if (i_ce) |
begin |
rl_op <= wl_op[3:0]; |
rl_break<= wl_break; |
rl_lock <= wl_lock; |
// |
rl_R <= (wl_R); |
rl_A <= (wl_A); |
rl_B <= (wl_B); |
// |
rl_ALU <= (~wl_op[4]); |
rl_MEM <= ( wl_op[4:1] == 4'h9); |
rl_DIV <= ( wl_op[4:1] == 4'ha); |
rl_FPU <= ( wl_op[4:2] == 3'h7)&&(i_instruction[30:28]==3'h7); |
rl_ljmp <= wl_ljmp; |
|
rl_pI <= wl_pI; |
rl_Iz<= wl_Iz; |
|
// What are the conditions of this instruction? |
rl_cond <= wl_cond; |
|
// Do we read register 'A'? |
rl_rA <= (wl_FPU)&&(wl_op[4:1]!=4'he) |
||(wl_DIV) |
||(wl_ALU)&&(~wf_mov) |
||(wl_MEM)&&(wl_op[0]) |
||(wl_cmptst); |
// Do we need to read register 'B'? |
rl_rB <= (i_instruction[18])&&(~wl_ldi); |
// Will we be writing register 'R'? |
rl_wR <= ~wl_wR_n; |
// How about the flags? |
rl_wF <= wl_wF; |
end |
|
////// |
// |
// Path 4: triplet instruction: MOV A,B; OP C,B |
// becomes OP C,A -> B |
// |
////// |
wire w_triplet; |
reg r_triplet; |
assign w_triplet = |
// Must be a VLIW instruction |
(i_instruction[31]) |
// First half must be a move |
&&(i_instruction[26:22] == 5'h0f) |
// Move destination must also be register A in 2nd Op |
&&(i_instruction[30:27]==i_instruction[13:10]) |
// Only if this is unconditional, or share conditions |
&&((i_instruction[21])|(i_instruction[20:19]==2'h0)) |
// Not if the destination is PC or CC regs |
&&(i_instruction[30:28]!=3'h7); |
always @(posedge i_clk) |
if (i_ce) |
r_triplet <= w_triplet; |
|
// Now, let's string our instruction(s) together to create a useful |
// decoded instruction |
reg r_singlet, r_gie; |
reg [(AW-1):0] r_pc; |
always @(posedge i_clk) |
if (i_ce) |
begin |
r_gie <= i_gie; |
o_gie<= r_gie; |
r_singlet <= ~i_instruction[31]; |
r_pc <= i_pc; |
|
if (r_triplet) |
begin |
o_phase <= 1'b0; |
o_pc <= r_pc + {{(AW-1){1'b0}}, 1'b1}; |
o_lock <= 1'b0; |
o_break <= 1'b0; |
o_R<={(rl_R=={r_gie, 4'he }),(rl_R=={r_gie,4'hf}),rl_R}; |
o_A<={(rh_B=={r_gie, 4'he }),(rh_B=={r_gie,4'hf}),rh_B}; |
o_B<={(rl_B=={r_gie, 4'he }),(rl_B=={r_gie,4'hf}),rl_B}; |
o_wR <= rl_wR; |
o_rA <= 1'b1; |
o_rB <= rl_rB; |
o_cond<= rh_cond; |
o_wF <= rl_wF; |
o_I <= wl_I; |
o_zI <= rl_Iz; |
o_op <= rl_op; |
o_ALU<= rl_ALU; |
o_M<= rl_MEM; |
o_DV<= rl_DIV; |
o_FP <= rl_FPU; |
o_ljmp <= 1'b0; |
o_early_branch <= 1'b0; |
o_branch_pc <= 0; |
// o_pipe will never be true for a triplet |
end else if (r_singlet) |
begin |
o_phase <= 1'b0; |
o_pc <= r_pc + {{(AW-1){1'b0}}, 1'b1}; |
o_R<={(rf_R=={r_gie, 4'he }),(rf_R=={r_gie,4'hf}),rf_R}; |
o_A<={(rf_A=={r_gie, 4'he }),(rf_A=={r_gie,4'hf}),rf_A}; |
o_B<={(rf_B=={r_gie, 4'he }),(rf_B=={r_gie,4'hf}),rf_B}; |
o_break <= rf_break; |
o_lock <= rf_lock; |
o_wR <= rf_wR; |
o_rA <= rf_rA; |
o_rB <= rf_rB; |
o_cond<= rf_cond; |
o_wF <= rf_wF; |
o_I <= wf_I; |
o_zI <= rf_Iz; |
o_op <= rf_op; |
o_ALU<= rf_ALU; |
o_M<= rf_MEM; |
o_DV<= rf_DIV; |
o_FP <= rf_FPU; |
o_ljmp <= rf_ljmp; |
o_early_branch <= (rf_ljmp); |
o_branch_pc <= i_instruction[(AW-1):0]; |
end else if (~o_phase) |
begin |
o_phase <= 1'b1; |
o_pc <= r_pc; |
o_R<={(rh_R=={r_gie, 4'he }),(rh_R=={r_gie,4'hf}),rh_R}; |
o_A<={(rh_A=={r_gie, 4'he }),(rh_A=={r_gie,4'hf}),rh_A}; |
o_B<={(rh_B=={r_gie, 4'he }),(rh_B=={r_gie,4'hf}),rh_B}; |
o_lock <= rh_lock; |
o_break <= rh_break; |
o_wR <= rh_wR; |
o_rA <= rh_rA; |
o_rB <= rh_rB; |
o_cond<= rh_cond; |
o_wF <= rh_wF; |
o_I <= wh_I; |
o_zI <= rh_Iz; |
o_op <= rh_op; |
o_ALU<= rh_ALU; |
o_M<= rh_MEM; |
o_DV<= rh_DIV; |
o_FP <= rh_FPU; |
o_ljmp <= 1'b0; // Can't jump from high-half |
o_early_branch <= 1'b0; |
// o_branch_pc <= i_instruction; |
end else begin |
o_phase <= 1'b0; |
o_pc <= r_pc + {{(AW-1){1'b0}}, 1'b1}; |
o_lock <= rl_lock; |
o_break <= rl_break; |
o_R<={(rl_R=={r_gie, 4'he }),(rl_R=={r_gie,4'hf}),rl_R}; |
o_A<={(rl_A=={r_gie, 4'he }),(rl_A=={r_gie,4'hf}),rl_A}; |
o_B<={(rl_B=={r_gie, 4'he }),(rl_B=={r_gie,4'hf}),rl_B}; |
o_wR <= rl_wR; |
o_rA <= rl_rA; |
o_rB <= rl_rB; |
o_cond<= rl_cond; |
o_wF <= rl_wF; |
o_I <= wl_I; |
o_zI <= rl_Iz; |
o_op <= rl_op; |
o_ALU<= rl_ALU; |
o_M<= rl_MEM; |
o_DV<= rl_DIV; |
o_FP <= rl_FPU; |
o_ljmp <= rl_ljmp; |
o_early_branch <= 1'b0; |
o_branch_pc <= i_instruction[(AW-1):0]; |
end |
o_illegal <= 1'b0; |
end |
|
/* |
always @(posedge i_clk) |
begin |
pipe_M <= { o_M, o_op[0] }; |
pipe_I <= o_I; |
pipe_pI <= o_I+1; |
pipe_B <= o_B[3:0]; |
pipe_rB<= o_rB; |
pipe_AB <= ({ o_A[6:5], o_B[6:5] } == 4'h00) // 12 inputs |
&&(o_A[3:0] != o_B[3:0]); |
pipe_gie<= o_gie; |
|
// 36 bits (18*2) |
match_sI <= (pipe_I == o_I); |
// 36 bits |
match_pI <= (pipe_pI == o_I); |
// 9 bits |
match_regs <= (pipe_M[1])&&(pipe_M == { o_M, o_op[0] }) |
&&((~pipe_rB)||(pipe_B == o_B[3:0])); |
// 9 inputs |
match_cnd <= ((pipe_cnd == o_cond)||(o_cond[3])); |
// 5 inputs |
match_aux <= (pipe_gie == o_gie)&&(pipeAB)&&(pipe_rB==o_rB) |
|
o_pipe <= ((match_sI)||(match_pI))&&(match_regs)&&(match_cnd) |
&&(match_aux)&&(pipe_AB); |
end |
*/ |
|
/* |
always @(posedge i_clk) |
if (i_rst) |
r_valid <= 1'b0; |
else if ((i_ce)&&(o_ljmp)) |
r_valid <= 1'b0; |
else if ((i_ce)&&(i_pf_valid)) |
r_valid <= 1'b1; |
else if (~i_stalled) |
r_valid <= 1'b0; |
*/ |
|
endmodule |
/rtl/cpu/div.v
0,0 → 1,195
/////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: div.v |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// Purpose: Provide an Integer divide capability to the Zip CPU. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////////// |
// |
// `include "cpudefs.v" |
// |
module div(i_clk, i_rst, i_wr, i_signed, i_numerator, i_denominator, |
o_busy, o_valid, o_err, o_quotient, o_flags); |
parameter BW=32, LGBW = 5; |
input i_clk, i_rst; |
// Input parameters |
input i_wr, i_signed; |
input [(BW-1):0] i_numerator, i_denominator; |
// Output parameters |
output reg o_busy, o_valid, o_err; |
output reg [(BW-1):0] o_quotient; |
output wire [3:0] o_flags; |
|
// r_busy is an internal busy register. It will clear one clock |
// 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; |
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] }; |
|
reg r_sign, pre_sign, r_z, r_c, last_bit; |
reg [(LGBW-1):0] r_bit; |
|
reg zero_divisor; |
initial zero_divisor = 1'b0; |
always @(posedge i_clk) |
zero_divisor <= (r_divisor == 0)&&(r_busy); |
|
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; |
|
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; |
|
always @(posedge i_clk) |
if ((i_rst)||(i_wr)) |
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; |
|
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; |
|
initial last_bit = 1'b0; |
always @(posedge i_clk) |
if ((i_wr)||(pre_sign)||(i_rst)) |
last_bit <= 1'b0; |
else if (r_busy) |
last_bit <= (r_bit == {{(LGBW-1){1'b0}},1'b1}); |
|
always @(posedge i_clk) |
// if (i_rst) r_busy <= 1'b0; |
// else |
if (i_wr) |
begin |
// |
// Set our values upon an initial command. Here's |
// where we come in and start. |
// |
// r_busy <= 1'b1; |
// |
o_quotient <= 0; |
r_bit <= {(LGBW){1'b1}}; |
r_divisor <= { i_denominator, {(BW-1){1'b0}} }; |
r_dividend <= i_numerator; |
r_sign <= 1'b0; |
pre_sign <= i_signed; |
r_z <= 1'b1; |
end else if (pre_sign) |
begin |
// |
// Note that we only come in here, for one clock, if |
// our initial value may have been signed. If we are |
// doing an unsigned divide, we then skip this step. |
// |
r_sign <= ((r_divisor[(2*BW-2)])^(r_dividend[(BW-1)])); |
// Negate our dividend if necessary so that it becomes |
// a magnitude only value |
if (r_dividend[BW-1]) |
r_dividend <= -r_dividend; |
// Do the same with the divisor--rendering it into |
// a magnitude only. |
if (r_divisor[(2*BW-2)]) |
r_divisor[(2*BW-2):(BW-1)] <= -r_divisor[(2*BW-2):(BW-1)]; |
// |
// We only do this stage for a single clock, so go on |
// with the rest of the divide otherwise. |
pre_sign <= 1'b0; |
end else if (r_busy) |
begin |
// While the divide is taking place, we examine each bit |
// in turn here. |
// |
r_bit <= r_bit + {(LGBW){1'b1}}; // r_bit = r_bit - 1; |
r_divisor <= { 1'b0, r_divisor[(2*BW-2):1] }; |
if (|r_divisor[(2*BW-2):(BW)]) |
begin |
end else if (diff[BW]) |
begin |
// |
// diff = r_dividend - r_divisor[(BW-1):0]; |
// |
// If this value was negative, there wasn't |
// enough value in the dividend to support |
// pulling off a bit. We'll move down a bit |
// therefore and try again. |
// |
end else begin |
// |
// Put a '1' into our output accumulator. |
// Subtract the divisor from the dividend, |
// and then move on to the next bit |
// |
r_dividend <= diff[(BW-1):0]; |
o_quotient[r_bit[(LGBW-1):0]] <= 1'b1; |
r_z <= 1'b0; |
end |
r_sign <= (r_sign)&&(~zero_divisor); |
end else if (r_sign) |
begin |
r_sign <= 1'b0; |
o_quotient <= -o_quotient; |
end |
|
// Set Carry on an exact divide |
wire w_n; |
always @(posedge i_clk) |
r_c <= (r_busy)&&((diff == 0)||(r_dividend == 0)); |
assign w_n = o_quotient[(BW-1)]; |
|
assign o_flags = { 1'b0, w_n, r_c, r_z }; |
endmodule |
/rtl/cpu/cpudefs.v
0,0 → 1,278
/////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: cpudefs.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: Some architectures have some needs, others have other needs. |
// Some of my projects need a Zip CPU with pipelining, others |
// can't handle the timing required to get the answer from the ALU |
// back into the input for the ALU. As each different projects has |
// different needs, I can either 1) reconfigure my entire baseline prior |
// to building each project, or 2) host a configuration file which contains |
// the information regarding each baseline. This file is that |
// configuration file. It controls how the CPU (not the system, |
// peripherals, or other) is defined and implemented. Several options |
// are available within here, making the Zip CPU pipelined or not, |
// able to handle a faster clock with more stalls or a slower clock with |
// no stalls, etc. |
// |
// This file encapsulates those control options. |
// |
// The number of LUTs the Zip CPU uses varies dramatically with the |
// options defined in this file. |
// |
// |
// OpenArty comments: |
// My goal on the OpenArty is going to be using the CPU to its fullest |
// extent. All features shall be turned on if they exist, full pipelines, |
// multiplies, divides, and hopefully even the 200MHz clock. This file |
// reflects that purpose. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////////// |
`ifndef CPUDEFS_H |
`define CPUDEFS_H |
// |
// |
// The first couple options control the Zip CPU instruction set, and how |
// 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, although it can be taken out if area is |
// critical ... |
// |
`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 |
// any parameter ...) If the multiply is not included and |
// OPT_ILLEGAL_INSTRUCTION is set, then the multiply will create an illegal |
// instruction that will then trip the illegal instruction trap. |
// |
// |
`define OPT_MULTIPLY 1 |
// |
// |
// |
// OPT_DIVIDE controls whether or not the divide instruction is built and |
// included into the ZipCPU by default. Set this option and a parameter will |
// be set that causes the divide unit to be included. (This parameter may |
// still be overridden, as with any parameter ...) If the divide is not |
// included and OPT_ILLEGAL_INSTRUCTION is set, then the multiply will create |
// an illegal instruction exception that will send the CPU into supervisor |
// mode. |
// |
// |
// `define OPT_DIVIDE |
// |
// |
// |
// OPT_IMPLEMENT_FPU will (one day) control whether or not the floating point |
// unit (once I have one) is built and included into the ZipCPU by default. |
// At that time, if this option is set then a parameter will be set that |
// causes the floating point unit to be included. (This parameter may |
// still be overridden, as with any parameter ...) If the floating point unit |
// is not included and OPT_ILLEGAL_INSTRUCTION is set, then as with the |
// multiply and divide any floating point instruction will result in an illegal |
// instruction exception that will send the CPU into supervisor mode. |
// |
// |
// `define OPT_IMPLEMENT_FPU |
// |
// |
// |
// OPT_NEW_INSTRUCTION_SET controls whether or not the new instruction set |
// is in use. The new instruction set contains space for floating point |
// operations, signed and unsigned divide instructions, as well as bit reversal |
// and ... at least two other operations yet to be defined. The decoder alone |
// uses about 70 fewer LUTs, although in practice this works out to 12 fewer |
// when all works out in the wash. Further, floating point and divide |
// instructions will cause an illegal instruction exception if they are not |
// implemented--so software capability can be built to use these instructions |
// immediately, even if the hardware is not yet ready. |
// |
// This option is likely to go away in the future, obsoleting the previous |
// instruction set, so I recommend setting this option and switching to the |
// new instruction set as soon as possible. |
// |
`define OPT_NEW_INSTRUCTION_SET |
// |
// |
// |
// |
// |
// |
// OPT_SINGLE_FETCH controls whether or not the prefetch has a cache, and |
// whether or not it can issue one instruction per clock. When set, the |
// prefetch has no cache, and only one instruction is fetched at a time. |
// This effectively sets the CPU so that only one instruction is ever |
// in the pipeline at once, and hence you may think of this as a "kill |
// pipeline" option. However, since the pipelined fetch component uses so |
// much area on the FPGA, this is an important option to use in trimming down |
// used area if necessary. Hence, it needs to be maintained for that purpose. |
// Be aware, though, it will drop your performance by a factor between 2x and |
// 3x. |
// |
// We can either pipeline our fetches, or issue one fetch at a time. Pipelined |
// fetches are more complicated and therefore use more FPGA resources, while |
// single fetches will cause the CPU to stall for about 5 stalls each |
// instruction cycle, effectively reducing the instruction count per clock to |
// about 0.2. However, the area cost may be worth it. Consider: |
// |
// Slice LUTs ZipSystem ZipCPU |
// Single Fetching 2521 1734 |
// Pipelined fetching 2796 2046 |
// (These numbers may be dated, but should still be representative ...) |
// |
// I recommend only defining this if you "need" to, if area is tight and |
// speed isn't as important. Otherwise, just leave this undefined. |
// |
// `define OPT_SINGLE_FETCH |
// |
// |
// |
// The next several options are pipeline optimization options. They make no |
// sense in a single instruction fetch mode, hence we #ifndef them so they |
// are only defined if we are in a full pipelined mode (i.e. OPT_SINGLE_FETCH |
// is not defined). |
// |
`ifndef OPT_SINGLE_FETCH |
// |
// |
// |
// OPT_PIPELINED is the natural result and opposite of using the single |
// instruction fetch unit. If you are not using that unit, the ZipCPU will |
// be pipelined. The option is defined here more for readability than |
// anything else, since OPT_PIPELINED makes more sense than OPT_SINGLE_FETCH, |
// well ... that and it does a better job of explaining what is going on. |
// |
// In other words, leave this define alone--lest you break the ZipCPU. |
// |
`define OPT_PIPELINED |
// |
// |
// |
// OPT_TRADITIONAL_PFCACHE allows you to switch between one of two prefetch |
// caches. If enabled, a more traditional cache is implemented. This more |
// traditional cache (currently) uses many more LUTs, but it also reduces |
// the stall count tremendously over the alternative hacked pipeline cache. |
// (The traditional pfcache is also pipelined, whereas the pipeline cache |
// implements a windowed approach to caching.) |
// |
// If you have the fabric to support this option, I recommend including it. |
// |
`define OPT_TRADITIONAL_PFCACHE |
// |
// |
// |
// OPT_EARLY_BRANCHING is an attempt to execute a BRA statement as early |
// as possible, to avoid as many pipeline stalls on a branch as possible. |
// It's not tremendously successful yet--BRA's still suffer stalls, |
// but I intend to keep working on this approach until the number of stalls |
// gets down to one or (ideally) zero. (With the OPT_TRADITIONAL_PFCACHE, this |
// gets down to a single stall cycle ...) That way a "BRA" can be used as the |
// compiler's branch prediction optimizer: BRA's barely stall, while branches |
// on conditions will always suffer about 4 stall cycles or so. |
// |
// I recommend setting this flag, so as to turn early branching on. |
// |
`define OPT_EARLY_BRANCHING |
// |
// |
// |
// 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 |
// only or stores only), and the addresses must either be identical or one up |
// from the previous address. Further, the load/store string must all have |
// the same conditional. This approach gains the must use, in my humble |
// opinion, when saving registers to or restoring registers from the stack |
// at the beginning/end of a procedure, or when doing a context swap. |
// |
// I recommend setting this flag, for performance reasons, especially if your |
// wishbone bus can handle pipelined bus accesses. |
// |
`define OPT_PIPELINED_BUS_ACCESS |
// |
// |
// |
`ifdef OPT_NEW_INSTRUCTION_SET |
// |
// |
// |
// The new instruction set also defines a set of very long instruction words. |
// Well, calling them "very long" instruction words is probably a misnomer, |
// although we're going to do it. They're really 2x16-bit instructions--- |
// instruction words that pack two instructions into one word. (2x14 bit |
// really--'cause you need a bit to note the instruction is a 2x instruction, |
// and then 3-bits for the condition codes ...) Set OPT_VLIW to include these |
// double instructions as part of the new instruction set. These allow a single |
// instruction to contain two instructions within. These instructions are |
// designed to get more code density from the instruction set, and to hopefully |
// take some pain off of the performance of the pre-fetch and instruction cache. |
// |
// These new instructions, however, also necessitate a change in the Zip |
// CPU--the Zip CPU can no longer execute instructions atomically. It must |
// now execute non-VLIW instructions, or VLIW instruction pairs, atomically. |
// This logic has been added into the ZipCPU, but it has not (yet) been |
// tested thoroughly. |
// |
// Oh, and the assembler, the debugger, and the object file dumper, and the |
// simulator all need to be updated as well .... |
// |
`define OPT_VLIW |
// |
// |
`endif // OPT_NEW_INSTRUCTION_SET |
// |
// |
`endif // OPT_SINGLE_FETCH |
// |
// |
// |
// 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 |
// include the respective peripherals, comment them out not to. |
// |
`define INCLUDE_DMA_CONTROLLER |
`define INCLUDE_ACCOUNTING_COUNTERS |
// |
// |
// `define DEBUG_SCOPE |
// |
`endif // CPUDEFS_H |
/rtl/cpu/pfcache.v
0,0 → 1,298
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: pfcache.v |
// |
// 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). |
// |
// 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. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
module pfcache(i_clk, i_rst, i_new_pc, i_clear_cache, |
// i_early_branch, i_from_addr, |
i_stall_n, i_pc, o_i, o_pc, o_v, |
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, |
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data, |
o_illegal); |
parameter LGCACHELEN = 8, ADDRESS_WIDTH=24, |
CACHELEN=(1<<LGCACHELEN), BUSW=32, AW=ADDRESS_WIDTH, |
CW=LGCACHELEN, PW=LGCACHELEN-5; |
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; |
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; |
// |
output reg o_illegal; |
|
// Fixed bus outputs: we read from the bus only, never write. |
// Thus the output data is ... irrelevant and don't care. We set it |
// to zero just to set it to something. |
assign o_wb_we = 1'b0; |
assign o_wb_data = 0; |
|
wire r_v; |
reg [(BUSW-1):0] cache [0:((1<<CW)-1)]; |
reg [(AW-CW-1):0] tags [0:((1<<(CW-PW))-1)]; |
reg [((1<<(CW-PW))-1):0] vmask; |
|
reg [(AW-1):0] lastpc; |
reg [(CW-1):0] rdaddr; |
reg [(AW-1):CW] tagvalipc, tagvallst; |
wire [(AW-1):CW] tagval; |
wire [(AW-1):PW] lasttag; |
reg illegal_valid; |
reg [(AW-1):PW] illegal_cache; |
|
// initial o_i = 32'h76_00_00_00; // A NOOP instruction |
// initial o_pc = 0; |
reg [(BUSW-1):0] r_pc_cache, r_last_cache; |
reg [(AW-1):0] r_pc, r_lastpc; |
reg isrc; |
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.) |
// |
// Here we keep track of which answer we want/need |
isrc <= ((r_v)&&(i_stall_n))||(i_new_pc); |
|
// Here we read both, and select which was write using isrc |
// on the next clock. |
r_pc_cache <= cache[i_pc[(CW-1):0]]; |
r_last_cache <= cache[lastpc[(CW-1):0]]; |
r_pc <= i_pc; |
r_lastpc <= lastpc; |
end |
assign o_pc = (isrc) ? r_pc : r_lastpc; |
assign o_i = (isrc) ? r_pc_cache : r_last_cache; |
|
reg tagsrc; |
always @(posedge i_clk) |
// It may be possible to recover a clock once the cache line |
// has been filled, but our prior attempt to do so has lead |
// to a race condition, so we keep this logic simple. |
if (((r_v)&&(i_stall_n))||(i_clear_cache)||(i_new_pc)) |
tagsrc <= 1'b1; |
else |
tagsrc <= 1'b0; |
initial tagvalipc = 0; |
always @(posedge i_clk) |
tagvalipc <= tags[i_pc[(CW-1):PW]]; |
initial tagvallst = 0; |
always @(posedge i_clk) |
tagvallst <= tags[lastpc[(CW-1):PW]]; |
assign tagval = (tagsrc)?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 |
// isn't valid, but the decoder is stalled, get the next address |
// anyway. |
initial lastpc = 0; |
always @(posedge i_clk) |
if (((r_v)&&(i_stall_n))||(i_clear_cache)||(i_new_pc)) |
lastpc <= i_pc; |
|
assign lasttag = lastpc[(AW-1):PW]; |
|
wire w_v_from_pc, w_v_from_last; |
assign w_v_from_pc = ((i_pc[(AW-1):PW] == lasttag) |
&&(tagvalipc == i_pc[(AW-1):CW]) |
&&(vmask[i_pc[(CW-1):PW]])); |
assign w_v_from_last = ( |
//(lastpc[(AW-1):PW] == lasttag)&& |
(tagval == lastpc[(AW-1):CW]) |
&&(vmask[lastpc[(CW-1):PW]])); |
|
reg [1:0] delay; |
|
initial delay = 2'h3; |
reg rvsrc; |
always @(posedge i_clk) |
if ((i_rst)||(i_clear_cache)||(i_new_pc)||((r_v)&&(i_stall_n))) |
begin |
// r_v <= r_v_from_pc; |
rvsrc <= 1'b1; |
delay <= 2'h2; |
end else if (~r_v) begin // Otherwise, r_v was true and we were |
// stalled, hence only if ~r_v |
rvsrc <= 1'b0; |
if (o_wb_cyc) |
delay <= 2'h2; |
else if (delay != 0) |
delay <= delay + 2'b11; // i.e. delay -= 1; |
end |
reg r_v_from_pc, r_v_from_last; |
always @(posedge i_clk) |
r_v_from_pc <= w_v_from_pc; |
always @(posedge i_clk) |
r_v_from_last <= w_v_from_last; |
|
assign r_v = ((rvsrc)?(r_v_from_pc):(r_v_from_last)); |
assign o_v = (((rvsrc)?(r_v_from_pc):(r_v_from_last)) |
||((o_illegal)&&(~o_wb_cyc))) |
&&(~i_new_pc)&&(~i_rst); |
|
reg last_ack; |
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))); |
|
reg needload; |
initial needload = 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))); |
|
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])); |
|
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; |
o_wb_stb <= 1'b0; |
end else if (o_wb_cyc) |
begin |
if (i_wb_err) |
o_wb_stb <= 1'b0; |
else if ((o_wb_stb)&&(~i_wb_stall)&&(last_addr)) |
o_wb_stb <= 1'b0; |
|
if (((i_wb_ack)&&(last_ack))||(i_wb_err)) |
o_wb_cyc <= 1'b0; |
|
// else if (rdaddr[(PW-1):1] == {(PW-1){1'b1}}) |
// tags[lastpc[(CW-1):PW]] <= lastpc[(AW-1):CW]; |
|
end else if (needload) |
begin |
o_wb_cyc <= 1'b1; |
o_wb_stb <= 1'b1; |
end |
|
always @(posedge i_clk) |
if (o_wb_cyc) // &&(i_wb_ack) |
tags[o_wb_addr[(CW-1):PW]] <= o_wb_addr[(AW-1):CW]; |
always @(posedge i_clk) |
if ((o_wb_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}} }; |
|
// Can't initialize an array, so leave cache uninitialized |
// We'll also never get an ack without sys being active, so skip |
// that check. Or rather, let's just use o_wb_cyc instead. This |
// will work because multiple writes to the same address, ending with |
// a valid write, aren't a problem. |
always @(posedge i_clk) |
if (o_wb_cyc) // &&(i_wb_ack) |
cache[rdaddr] <= 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; |
initial svmask = 1'b0; |
reg [(CW-PW-1):0] saddr; |
always @(posedge i_clk) |
if ((i_rst)||(i_clear_cache)) |
begin |
vmask <= 0; |
svmask<= 1'b0; |
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; |
end |
always @(posedge i_clk) |
if ((o_wb_cyc)&&(i_wb_ack)) |
saddr <= rdaddr[(CW-1):PW]; |
|
initial illegal_cache = 0; |
initial illegal_valid = 0; |
always @(posedge i_clk) |
if ((i_rst)||(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):PW]; |
illegal_valid <= 1'b1; |
end |
|
initial o_illegal = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(i_clear_cache)||(o_wb_cyc)) |
o_illegal <= 1'b0; |
else |
o_illegal <= (illegal_valid) |
&&(illegal_cache == i_pc[(AW-1):PW]); |
|
endmodule |
/rtl/cpu/zipbones.v
0,0 → 1,215
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: zipbones.v |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// 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) 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. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
`include "cpudefs.v" |
// |
module zipbones(i_clk, i_rst, |
// Wishbone master interface from the CPU |
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, |
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err, |
// Incoming interrupts |
i_ext_int, |
// Our one outgoing interrupt |
o_ext_int, |
// Wishbone slave interface for debugging purposes |
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 |
`endif |
); |
parameter RESET_ADDRESS=32'h0100000, ADDRESS_WIDTH=32, |
LGICACHE=6, START_HALTED=0, |
AW=ADDRESS_WIDTH, HIGHSPEED_CPU=1; |
input i_clk, i_rst; |
// Wishbone master |
output wire o_wb_cyc, o_wb_stb, o_wb_we; |
output wire [(AW-1):0] o_wb_addr; |
output wire [31:0] o_wb_data; |
input i_wb_ack, i_wb_stall; |
input [31:0] i_wb_data; |
input i_wb_err; |
// Incoming interrupts |
input 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; |
output wire o_dbg_stall; |
output wire [31:0] o_dbg_data; |
// |
`ifdef DEBUG_SCOPE |
output wire [31:0] o_zip_debug; |
`endif |
|
// |
// |
// |
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 |
// write to set the local address. This interface allows access to |
// the Zip System on a debug basis only, and not to the rest of the |
// wishbone bus. Further, to access these registers, the control |
// register must first be accessed to both stop the CPU and to |
// set the following address in question. Hence all accesses require |
// two accesses: write the address to the control register (and halt |
// the CPU if not halted), then read/write the data from the data |
// register. |
// |
wire cpu_break, dbg_cmd_write; |
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); |
// |
// 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])); |
// |
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; |
|
initial cmd_clear_pf_cache = 1'b0; |
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; |
// |
initial cmd_step = 1'b0; |
always @(posedge i_clk) |
cmd_step <= (dbg_cmd_write)&&(i_dbg_data[8]); |
// |
initial cmd_addr = 5'h0; |
always @(posedge i_clk) |
if (dbg_cmd_write) |
cmd_addr <= i_dbg_data[4:0]; |
|
wire cpu_reset; |
assign cpu_reset = (cmd_reset)||(i_rst); |
|
wire cpu_halt, cpu_dbg_stall; |
assign cpu_halt = (i_rst)||((cmd_halt)&&(~cmd_step)); |
wire [31:0] cmd_data; |
// Values: |
// 0x0003f -> cmd_addr mask |
// 0x00040 -> reset |
// 0x00080 -> PIC interrrupts enabled |
// 0x00100 -> cmd_step |
// 0x00200 -> cmd_stall |
// 0x00400 -> cmd_halt |
// 0x00800 -> cmd_clear_pf_cache |
// 0x01000 -> cc.sleep |
// 0x02000 -> cc.gie |
// 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 }; |
|
// |
// The CPU itself |
// |
wire cpu_gbl_stb, cpu_lcl_cyc, cpu_lcl_stb, |
cpu_we, cpu_dbg_we, |
cpu_op_stall, cpu_pf_stall, cpu_i_count; |
wire [31:0] cpu_data; |
wire [31:0] cpu_dbg_data; |
assign cpu_dbg_we = ((i_dbg_cyc)&&(i_dbg_stb) |
&&(i_dbg_we)&&(i_dbg_addr)); |
generate |
if (HIGHSPEED_CPU==0) |
begin |
zipcpu #(RESET_ADDRESS,ADDRESS_WIDTH,LGICACHE) |
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, |
cpu_dbg_cc, cpu_break, |
o_wb_cyc, o_wb_stb, |
cpu_lcl_cyc, cpu_lcl_stb, |
o_wb_we, o_wb_addr, o_wb_data, |
i_wb_ack, i_wb_stall, i_wb_data, |
(i_wb_err)||((cpu_lcl_cyc)&&(cpu_lcl_stb)), |
cpu_op_stall, cpu_pf_stall, cpu_i_count |
`ifdef DEBUG_SCOPE |
, o_zip_debug |
`endif |
); |
end else begin |
zipcpuhs #(RESET_ADDRESS,ADDRESS_WIDTH,LGICACHE) |
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, |
cpu_dbg_cc, cpu_break, |
o_wb_cyc, o_wb_stb, |
cpu_lcl_cyc, cpu_lcl_stb, |
o_wb_we, o_wb_addr, o_wb_data, |
i_wb_ack, i_wb_stall, i_wb_data, |
(i_wb_err)||((cpu_lcl_cyc)&&(cpu_lcl_stb)), |
cpu_op_stall, cpu_pf_stall, cpu_i_count |
`ifdef DEBUG_SCOPE |
, o_zip_debug |
`endif |
); |
end endgenerate |
|
// Return debug response values |
assign o_dbg_data = (~i_dbg_addr)?cmd_data :cpu_dbg_data; |
initial o_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); |
|
assign o_ext_int = (cmd_halt) && (~i_wb_stall); |
|
endmodule |
/rtl/cpu/icontrol.v
0,0 → 1,153
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: icontrol.v |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// Purpose: An interrupt controller, for managing many interrupt sources. |
// |
// This interrupt controller started from the question of how best to |
// design a simple interrupt controller. As such, it has a few nice |
// qualities to it: |
// 1. This is wishbone compliant |
// 2. It sits on a 32-bit wishbone data bus |
// 3. It only consumes one address on that wishbone bus. |
// 4. There is no extra delays associated with reading this |
// device. |
// 5. Common operations can all be done in one clock. |
// |
// So, how shall this be used? First, the 32-bit word is broken down as |
// follows: |
// |
// Bit 31 - This is the global interrupt enable bit. If set, interrupts |
// will be generated and passed on as they come in. |
// Bits 16-30 - These are specific interrupt enable lines. If set, |
// interrupts from source (bit#-16) will be enabled. |
// To set this line and enable interrupts from this source, write |
// to the register with this bit set and the global enable set. |
// To disable this line, write to this register with global enable |
// bit not set, but this bit set. (Writing a zero to any of these |
// bits has no effect, either setting or unsetting them.) |
// Bit 15 - This is the any interrupt pin. If any interrupt is pending, |
// this bit will be set. |
// Bits 0-14 - These are interrupt bits. When set, an interrupt is |
// pending from the corresponding source--regardless of whether |
// it was enabled. (If not enabled, it won't generate an |
// interrupt, but it will still register here.) To clear any |
// of these bits, write a '1' to the corresponding bit. Writing |
// a zero to any of these bits has no effect. |
// |
// The peripheral also sports a parameter, IUSED, which can be set |
// to any value between 1 and (buswidth/2-1, or) 15 inclusive. This will |
// be the number of interrupts handled by this routine. (Without the |
// parameter, Vivado was complaining about unused bits. With it, we can |
// keep the complaints down and still use the routine). |
// |
// To get access to more than 15 interrupts, chain these together, so |
// that one interrupt controller device feeds another. |
// |
// |
// 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. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
module icontrol(i_clk, i_reset, i_wr, i_proc_bus, o_proc_bus, |
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; |
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; |
|
assign nxt_int_state = (r_int_state|i_brd_ints); |
initial r_int_state = 0; |
always @(posedge i_clk) |
if (i_reset) |
r_int_state <= 0; |
else if (i_wr) |
r_int_state <= nxt_int_state & (~i_proc_bus[(IUSED-1):0]); |
else |
r_int_state <= nxt_int_state; |
initial r_int_enable = 0; |
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]); |
|
initial r_gie = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
r_gie <= 1'b0; |
else if (i_wr) |
r_gie <= i_proc_bus[31]; |
|
initial r_any = 1'b0; |
always @(posedge i_clk) |
r_any <= ((r_int_state & r_int_enable) != 0); |
initial r_interrupt = 1'b0; |
always @(posedge i_clk) |
r_interrupt <= r_gie & r_any; |
|
generate |
if (IUSED < 15) |
begin |
assign o_proc_bus = { |
r_gie, { {(15-IUSED){1'b0}}, r_int_enable }, |
r_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 }; |
end endgenerate |
|
/* |
reg int_condition; |
initial int_condition = 1'b0; |
initial o_interrupt_strobe = 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; |
*/ |
|
assign o_interrupt = r_interrupt; |
|
endmodule |
/rtl/cpu/wbwatchdog.v
0,0 → 1,76
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbwatchdog.v |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// 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. |
// |
// As a historical note, the wishbone watchdog timer began as a normal |
// timer, with some fixed inputs. This makes sense, if you think about it: |
// if the goal is to interrupt a stalled wishbone transaction by inserting |
// a bus error, then you can't use the bus to set it up or configure it |
// simply because the bus in question is ... well, unreliable. You're |
// trying to make it reliable. |
// |
// The problem with using the ziptimer in a stripped down implementation |
// was that the fixed inputs caused the synthesis tool to complain about |
// the use of registers values would never change. This solves that |
// problem by explicitly removing the cruft that would otherwise |
// just create synthesis warnings and errors. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015, 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
module wbwatchdog(i_clk, i_rst, i_ce, i_timeout, o_int); |
parameter BW = 32; |
input i_clk, i_rst, i_ce; |
// Inputs (these were at one time wishbone controlled ...) |
input [(BW-1):0] i_timeout; |
// Interrupt line |
output reg o_int; |
|
reg [(BW-1):0] r_value; |
initial r_value = 0; |
always @(posedge i_clk) |
if (i_rst) |
r_value <= i_timeout[(BW-1):0]; |
else if ((i_ce)&&(~o_int)) |
r_value <= r_value + {(BW){1'b1}}; // r_value - 1; |
|
// Set the interrupt on our last tick. |
initial o_int = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(~i_ce)) |
o_int <= 1'b0; |
else |
o_int <= (r_value == { {(BW-1){1'b0}}, 1'b1 }); |
|
endmodule |
/rtl/cpu/wbdblpriarb.v
0,0 → 1,142
/////////////////////////////////////////////////////////////////////////// |
// |
// 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, 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
module wbdblpriarb(i_clk, i_rst, |
// 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, 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, 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, |
i_ack, i_stall, i_err); |
parameter DW=32, AW=32; |
// Wishbone doesn't use an i_ce signal. While it could, they dislike |
// what it would (might) do to the synchronous reset signal, i_rst. |
input i_clk, i_rst; |
// Bus A |
input i_a_cyc_a, i_a_cyc_b, i_a_stb_a, i_a_stb_b, i_a_we; |
input [(AW-1):0] i_a_adr; |
input [(DW-1):0] i_a_dat; |
output wire o_a_ack, o_a_stall, o_a_err; |
// Bus B |
input i_b_cyc_a, i_b_cyc_b, i_b_stb_a, i_b_stb_b, i_b_we; |
input [(AW-1):0] i_b_adr; |
input [(DW-1):0] i_b_dat; |
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; |
input i_ack, i_stall, i_err; |
|
// 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. |
assign o_cyc_a = (~i_rst)&&((r_a_owner) ? i_a_cyc_a : i_b_cyc_a); |
assign o_cyc_b = (~i_rst)&&((r_a_owner) ? i_a_cyc_b : i_b_cyc_b); |
reg r_a_owner; |
initial r_a_owner = 1'b1; |
always @(posedge i_clk) |
if (i_rst) |
r_a_owner <= 1'b1; |
else if ((~o_cyc_a)&&(~o_cyc_b)) |
r_a_owner <= ((i_b_cyc_a)||(i_b_cyc_b))? 1'b0:1'b1; |
|
|
// 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_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; |
assign o_adr = (r_a_owner) ? i_a_adr : i_b_adr; |
assign o_dat = (r_a_owner) ? i_a_dat : i_b_dat; |
|
// 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; |
|
// |
// These error lines will be implemented soon, as soon as the rest of |
// the Zip CPU is ready to support them. |
// |
assign o_a_err = ( r_a_owner) ? i_err : 1'b0; |
assign o_b_err = (~r_a_owner) ? i_err : 1'b0; |
|
endmodule |
|
/rtl/cpu/pipemem.v
0,0 → 1,196
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: pipemem.v |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// Purpose: A memory unit to support a CPU, this time one supporting |
// pipelined wishbone memory accesses. The goal is to be able |
// to issue one pipelined wishbone access per clock, and (given the memory |
// is fast enough) to be able to read the results back at one access per |
// clock. This renders on-chip memory fast enough to handle single cycle |
// (pipelined) access. |
// |
// |
// 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. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
module pipemem(i_clk, i_rst, i_pipe_stb, i_lock, |
i_op, i_addr, i_data, i_oreg, |
o_busy, o_pipe_stalled, o_valid, o_err, o_wreg, o_result, |
o_wb_cyc_gbl, o_wb_cyc_lcl, |
o_wb_stb_gbl, o_wb_stb_lcl, |
o_wb_we, o_wb_addr, o_wb_data, |
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data); |
parameter ADDRESS_WIDTH=24, IMPLEMENT_LOCK=0, AW=ADDRESS_WIDTH; |
input i_clk, i_rst; |
input i_pipe_stb, i_lock; |
// CPU interface |
input i_op; |
input [31:0] i_addr; |
input [31:0] i_data; |
input [4:0] i_oreg; |
// CPU outputs |
output wire o_busy; |
output reg o_pipe_stalled; |
output reg o_valid; |
output reg o_err; |
output reg [4:0] o_wreg; |
output reg [31:0] o_result; |
// Wishbone outputs |
output wire o_wb_cyc_gbl; |
output reg o_wb_stb_gbl; |
output wire o_wb_cyc_lcl; |
output reg o_wb_stb_lcl, o_wb_we; |
output reg [(AW-1):0] o_wb_addr; |
output reg [31:0] o_wb_data; |
// Wishbone inputs |
input i_wb_ack, i_wb_stall, i_wb_err; |
input [31:0] i_wb_data; |
|
reg cyc; |
reg r_wb_cyc_gbl, r_wb_cyc_lcl; |
reg [3:0] rdaddr, wraddr; |
wire [3:0] nxt_rdaddr; |
reg [(5-1):0] fifo_oreg [0:15]; |
initial rdaddr = 0; |
initial wraddr = 0; |
always @(posedge i_clk) |
fifo_oreg[wraddr] <= i_oreg; |
always @(posedge i_clk) |
if ((i_rst)||(i_wb_err)) |
wraddr <= 0; |
else if (i_pipe_stb) |
wraddr <= wraddr + 4'h1; |
always @(posedge i_clk) |
if ((i_rst)||(i_wb_err)) |
rdaddr <= 0; |
else if ((i_wb_ack)&&(cyc)) |
rdaddr <= rdaddr + 4'h1; |
assign nxt_rdaddr = rdaddr + 4'h1; |
|
wire gbl_stb, lcl_stb; |
assign lcl_stb = (i_addr[31:8]==24'hc00000)&&(i_addr[7:5]==3'h0); |
assign gbl_stb = (~lcl_stb); |
//= ((i_addr[31:8]!=24'hc00000)||(i_addr[7:5]!=3'h0)); |
|
initial cyc = 0; |
initial r_wb_cyc_lcl = 0; |
initial r_wb_cyc_gbl = 0; |
always @(posedge i_clk) |
if (i_rst) |
begin |
r_wb_cyc_gbl <= 1'b0; |
r_wb_cyc_lcl <= 1'b0; |
o_wb_stb_gbl <= 1'b0; |
o_wb_stb_lcl <= 1'b0; |
cyc <= 1'b0; |
end else if (cyc) |
begin |
if ((~i_wb_stall)&&(~i_pipe_stb)) |
begin |
o_wb_stb_gbl <= 1'b0; |
o_wb_stb_lcl <= 1'b0; |
// end else if ((i_pipe_stb)&&(~i_wb_stall)) |
// begin |
// o_wb_addr <= i_addr[(AW-1):0]; |
// o_wb_data <= i_data; |
end |
|
if (((i_wb_ack)&&(nxt_rdaddr == wraddr))||(i_wb_err)) |
begin |
r_wb_cyc_gbl <= 1'b0; |
r_wb_cyc_lcl <= 1'b0; |
cyc <= 1'b0; |
end |
end else if (i_pipe_stb) // New memory operation |
begin // Grab the wishbone |
r_wb_cyc_lcl <= lcl_stb; |
r_wb_cyc_gbl <= gbl_stb; |
o_wb_stb_lcl <= lcl_stb; |
o_wb_stb_gbl <= gbl_stb; |
cyc <= 1'b1; |
// o_wb_addr <= i_addr[(AW-1):0]; |
// o_wb_data <= i_data; |
// o_wb_we <= i_op |
end |
always @(posedge i_clk) |
if ((cyc)&&(i_pipe_stb)&&(~i_wb_stall)) |
begin |
o_wb_addr <= i_addr[(AW-1):0]; |
o_wb_data <= i_data; |
end else if ((~cyc)&&(i_pipe_stb)) |
begin |
o_wb_addr <= i_addr[(AW-1):0]; |
o_wb_data <= i_data; |
end |
|
always @(posedge i_clk) |
if ((i_pipe_stb)&&(~cyc)) |
o_wb_we <= i_op; |
|
initial o_valid = 1'b0; |
always @(posedge i_clk) |
o_valid <= (cyc)&&(i_wb_ack)&&(~o_wb_we); |
initial o_err = 1'b0; |
always @(posedge i_clk) |
o_err <= (cyc)&&(i_wb_err); |
assign o_busy = cyc; |
|
always @(posedge i_clk) |
o_wreg <= fifo_oreg[rdaddr]; |
always @(posedge i_clk) |
// if (i_wb_ack) isn't necessary, since o_valid won't be true |
// then either. |
o_result <= i_wb_data; |
|
/* |
assign o_pipe_stalled = (cyc) |
&&((i_wb_stall)||((~o_wb_stb_lcl)&&(~o_wb_stb_gbl))); |
*/ |
always @(posedge i_clk) |
o_pipe_stalled <= (i_pipe_stb)&&(cyc)&&(i_wb_stall); |
|
|
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) |
begin |
lock_gbl <= (i_lock)&&((r_wb_cyc_gbl)||(lock_gbl)); |
lock_lcl <= (i_lock)&&((r_wb_cyc_lcl)||(lock_gbl)); |
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); |
end endgenerate |
|
endmodule |
/rtl/cpu/idecode.v
0,0 → 1,489
/////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: idecode.v |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// Purpose: This RTL file specifies how instructions are to be decoded |
// into their underlying meanings. This is specifically a version |
// designed to support a "Next Generation", or "Version 2" instruction |
// set as (currently) activated by the OPT_NEW_INSTRUCTION_SET option |
// in cpudefs.v. |
// |
// I expect to (eventually) retire the old instruction set, at which point |
// this will become the default instruction set decoder. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////////// |
// |
// |
// |
`define CPU_CC_REG 4'he |
`define CPU_PC_REG 4'hf |
// |
`include "cpudefs.v" |
// |
// |
// |
module idecode(i_clk, i_rst, i_ce, i_stalled, |
i_instruction, i_gie, i_pc, i_pf_valid, |
i_illegal, |
o_phase, o_illegal, |
o_pc, o_gie, |
o_dcdR, o_dcdA, o_dcdB, o_I, o_zI, |
o_cond, o_wF, |
o_op, o_ALU, o_M, o_DV, o_FP, o_break, o_lock, |
o_wR, o_rA, o_rB, |
o_early_branch, o_branch_pc, o_ljmp, |
o_pipe |
); |
parameter ADDRESS_WIDTH=24, IMPLEMENT_MPY=1, EARLY_BRANCHING=1, |
IMPLEMENT_DIVIDE=1, IMPLEMENT_FPU=0, AW = ADDRESS_WIDTH; |
input i_clk, i_rst, i_ce, i_stalled; |
input [31:0] i_instruction; |
input i_gie; |
input [(AW-1):0] i_pc; |
input i_pf_valid, i_illegal; |
output wire o_phase; |
output reg o_illegal; |
output reg [(AW-1):0] o_pc; |
output reg o_gie; |
output reg [6:0] o_dcdR, o_dcdA, o_dcdB; |
output wire [31:0] o_I; |
output reg o_zI; |
output reg [3:0] o_cond; |
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_wR, o_rA, o_rB; |
output wire o_early_branch; |
output wire [(AW-1):0] o_branch_pc; |
output wire o_ljmp; |
output wire o_pipe; |
|
wire dcdA_stall, dcdB_stall, dcdF_stall; |
wire o_dcd_early_branch; |
wire [(AW-1):0] o_dcd_branch_pc; |
reg o_dcdI, o_dcdIz; |
`ifdef OPT_PIPELINED |
reg r_lock; |
`endif |
`ifdef OPT_PIPELINED_BUS_ACCESS |
reg r_pipe; |
`endif |
|
|
wire [4:0] w_op; |
wire w_ldi, w_mov, w_cmptst, w_ldilo, w_ALU, w_brev, w_noop; |
wire [4:0] w_dcdR, w_dcdB, w_dcdA; |
wire w_dcdR_pc, w_dcdR_cc; |
wire w_dcdA_pc, w_dcdA_cc; |
wire w_dcdB_pc, w_dcdB_cc; |
wire [3:0] w_cond; |
wire w_wF, w_dcdM, w_dcdDV, w_dcdFP; |
wire w_wR, w_rA, w_rB, w_wR_n; |
wire w_ljmp, w_ljmp_dly; |
wire [31:0] iword; |
|
|
`ifdef OPT_VLIW |
reg [16:0] r_nxt_half; |
assign iword = (o_phase) |
// set second half as a NOOP ... but really |
// shouldn't matter |
? { r_nxt_half[16:7], 1'b0, r_nxt_half[6:0], 5'b11000, 3'h7, 6'h00 } |
: i_instruction; |
`else |
assign iword = { 1'b0, i_instruction[30:0] }; |
`endif |
|
generate |
if (EARLY_BRANCHING != 0) |
assign w_ljmp = (iword == 32'h7c87c000); |
else |
assign w_ljmp = 1'b0; |
endgenerate |
|
|
assign w_op= iword[26:22]; |
assign w_mov = (w_op == 5'h0f); |
assign w_ldi = (w_op[4:1] == 4'hb); |
assign w_brev = (w_op == 5'hc); |
assign w_cmptst = (w_op[4:1] == 4'h8); |
assign w_ldilo = (w_op[4:0] == 5'h9); |
assign w_ALU = (~w_op[4]); |
|
// 4 LUTs |
// |
// Two parts to the result register: the register set, given for |
// moves in i_word[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, |
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_noop = (w_op[4:0] == 5'h18)&&( |
((IMPLEMENT_FPU>0)&&(w_dcdR[3:1] == 3'h7)) |
||(IMPLEMENT_FPU==0)); |
|
// 4 LUTs |
assign w_dcdB = { ((~iword[31])&&(w_mov)&&(~i_gie))?iword[13]:i_gie, |
iword[17:14] }; |
|
// 0 LUTs |
assign w_dcdA = w_dcdR; |
// 2 LUTs, 1 delay each |
assign w_dcdR_pc = (w_dcdR == {i_gie, `CPU_PC_REG}); |
assign w_dcdR_cc = (w_dcdR == {i_gie, `CPU_CC_REG}); |
// 0 LUTs |
assign w_dcdA_pc = w_dcdR_pc; |
assign w_dcdA_cc = w_dcdR_cc; |
// 2 LUTs, 1 delays each |
assign w_dcdB_pc = (w_dcdB[3:0] == `CPU_PC_REG); |
assign w_dcdB_cc = (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) ? 4'h8 : |
(iword[31])?{(iword[20:19]==2'b00), |
1'b0,iword[20:19]} |
: { (iword[21:19]==3'h0), iword[21:19] }; |
|
// 1 LUT |
assign w_dcdM = (w_op[4:1] == 4'h9); |
// 1 LUT |
assign w_dcdDV = (w_op[4:1] == 4'ha); |
// 1 LUT |
assign w_dcdFP = (w_op[4:3] == 2'b11)&&(w_dcdR[3:1] != 3'h7); |
// 4 LUT's--since it depends upon FP/NOOP condition (vs 1 before) |
// Everything reads A but ... NOOP/BREAK/LOCK, LDI, LOD, MOV |
assign w_rA = (w_dcdFP) |
// Divide's read A |
||(w_dcdDV) |
// ALU read's A, unless it's a MOV to A |
// This includes LDIHI/LDILO |
||((~w_op[4])&&(w_op[3:0]!=4'hf)) |
// STO's read A |
||((w_dcdM)&&(w_op[0])) |
// Test/compares |
||(w_op[4:1]== 4'h8); |
// 1 LUTs -- do we read a register for operand B? Specifically, do |
// we need to stall if the register is not (yet) ready? |
assign w_rB = (w_mov)||((iword[18])&&(~w_ldi)); |
// 1 LUT: All but STO, NOOP/BREAK/LOCK, and CMP/TST write back to w_dcdR |
assign w_wR_n = ((w_dcdM)&&(w_op[0])) |
||((w_op[4:3]==2'b11)&&(w_dcdR[3:1]==3'h7)) |
||(w_cmptst); |
assign w_wR = ~w_wR_n; |
// |
// 1-output bit (5 Opcode bits, 4 out-reg bits, 3 condition bits) |
// |
// This'd be 4 LUTs, save that we have the carve out for NOOPs |
// and writes to the PC/CC register(s). |
assign w_wF = (w_cmptst) |
||((w_cond[3])&&((w_dcdFP)||(w_dcdDV) |
||((w_ALU)&&(~w_mov)&&(~w_ldilo)&&(~w_brev) |
&&(iword[30:28] != 3'h7)))); |
|
// Bottom 13 bits: no LUT's |
// w_dcd[12: 0] -- no LUTs |
// w_dcd[ 13] -- 2 LUTs |
// w_dcd[17:14] -- (5+i0+i1) = 3 LUTs, 1 delay |
// w_dcd[22:18] : 5 LUTs, 1 delay (assuming high bit is o/w determined) |
reg [22:0] r_I; |
wire [22:0] w_I, w_fullI; |
wire w_Iz; |
|
assign w_fullI = (w_ldi) ? { iword[22:0] } // LDI |
:((w_mov) ?{ {(23-13){iword[12]}}, iword[12:0] } // Move |
:((~iword[18]) ? { {(23-18){iword[17]}}, iword[17:0] } |
: { {(23-14){iword[13]}}, iword[13:0] } |
)); |
|
`ifdef OPT_VLIW |
wire [5:0] w_halfI; |
assign w_halfI = (w_ldi) ? iword[5:0] |
:((iword[5]) ? 6'h00 : {iword[4],iword[4:0]}); |
assign w_I = (iword[31])? {{(23-6){w_halfI[5]}}, w_halfI }:w_fullI; |
`else |
assign w_I = w_fullI; |
`endif |
assign w_Iz = (w_I == 0); |
|
|
`ifdef OPT_VLIW |
// |
// The o_phase parameter is special. It needs to let the software |
// following know that it cannot break/interrupt on an o_phase asserted |
// instruction, lest the break take place between the first and second |
// half of a VLIW 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)) |
r_phase <= 1'b0; |
else if ((i_ce)&&(i_pf_valid)) |
r_phase <= (o_phase)? 1'b0:(i_instruction[31]); |
// 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 |
|
|
initial o_illegal = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
o_illegal <= 1'b0; |
else if (i_ce) |
begin |
`ifdef OPT_VLIW |
o_illegal <= (i_illegal); |
`else |
o_illegal <= ((i_illegal) || (i_instruction[31])); |
`endif |
if ((IMPLEMENT_MPY==0)&&((w_op[4:1]==4'h5)||(w_op[4:0]==5'h08))) |
o_illegal <= 1'b1; |
|
if ((IMPLEMENT_DIVIDE==0)&&(w_dcdDV)) |
o_illegal <= 1'b1; |
else if ((IMPLEMENT_DIVIDE!=0)&&(w_dcdDV)&&(w_dcdR[3:1]==3'h7)) |
o_illegal <= 1'b1; |
|
|
if ((IMPLEMENT_FPU!=0)&&(w_dcdFP)&&(w_dcdR[3:1]==3'h7)) |
o_illegal <= 1'b1; |
else if ((IMPLEMENT_FPU==0)&&(w_dcdFP)) |
o_illegal <= 1'b1; |
|
if ((w_op[4:3]==2'b11)&&(w_dcdR[3:1]==3'h7) |
&&( |
(w_op[2:0] != 3'h1) // BREAK |
`ifdef OPT_PIPELINED |
&&(w_op[2:0] != 3'h2) // LOCK |
`endif |
&&(w_op[2:0] != 3'h0))) // NOOP |
o_illegal <= 1'b1; |
end |
|
|
always @(posedge i_clk) |
if (i_ce) |
begin |
`ifdef OPT_VLIW |
if (~o_phase) |
begin |
o_gie<= i_gie; |
// i.e. dcd_pc+1 |
o_pc <= i_pc+{{(AW-1){1'b0}},1'b1}; |
end |
`else |
o_gie<= i_gie; |
o_pc <= i_pc+{{(AW-1){1'b0}},1'b1}; |
`endif |
|
// 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'hf:w_op[3:0]; |
|
// Default values |
o_dcdR <= { w_dcdR_cc, w_dcdR_pc, w_dcdR}; |
o_dcdA <= { w_dcdA_cc, w_dcdA_pc, w_dcdA}; |
o_dcdB <= { w_dcdB_cc, w_dcdB_pc, w_dcdB}; |
o_wR <= w_wR; |
o_rA <= w_rA; |
o_rB <= w_rB; |
r_I <= w_I; |
o_zI <= w_Iz; |
|
// 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); // 2 LUT |
o_M <= w_dcdM; |
o_DV <= w_dcdDV; |
o_FP <= w_dcdFP; |
|
o_break <= (w_op[4:0]==5'b11001)&&( |
((IMPLEMENT_FPU>0)&&(w_dcdR[3:1]==3'h7)) |
||(IMPLEMENT_FPU==0)); |
`ifdef OPT_PIPELINED |
r_lock <= (w_op[4:0]==5'b11010)&&( |
((IMPLEMENT_FPU>0)&&(w_dcdR[3:1]==3'h7)) |
||(IMPLEMENT_FPU==0)); |
`endif |
`ifdef OPT_VLIW |
r_nxt_half <= { iword[31], iword[13:5], |
((iword[21])? iword[20:19] : 2'h0), |
iword[4:0] }; |
`endif |
end |
|
`ifdef OPT_PIPELINED |
assign o_lock = r_lock; |
`else |
assign o_lock = 1'b0; |
`endif |
|
generate |
if (EARLY_BRANCHING!=0) |
begin |
reg r_early_branch, r_ljmp; |
reg [(AW-1):0] r_branch_pc; |
|
initial r_ljmp = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
r_ljmp <= 1'b0; |
else if ((i_ce)&&(i_pf_valid)) |
r_ljmp <= (w_ljmp); |
assign o_ljmp = r_ljmp; |
|
always @(posedge i_clk) |
if (i_rst) |
r_early_branch <= 1'b0; |
else if ((i_ce)&&(i_pf_valid)) |
begin |
if (r_ljmp) |
// LOD (PC),PC |
r_early_branch <= 1'b1; |
else if ((~iword[31])&&(iword[30:27]==`CPU_PC_REG)&&(w_cond[3])) |
begin |
if (w_op[4:1] == 4'hb) // LDI to PC |
// LDI x,PC |
r_early_branch <= 1'b1; |
else if ((w_op[4:0]==5'h02)&&(~iword[18])) |
// Add x,PC |
r_early_branch <= 1'b1; |
else begin |
r_early_branch <= 1'b0; |
end |
end else |
r_early_branch <= 1'b0; |
end else if (i_ce) |
r_early_branch <= 1'b0; |
|
always @(posedge i_clk) |
if (i_ce) |
begin |
if (r_ljmp) |
r_branch_pc <= iword[(AW-1):0]; |
else if (w_op[4:1] == 4'hb) // LDI |
r_branch_pc <= {{(AW-23){iword[22]}},iword[22:0]}; |
else // Add x,PC |
r_branch_pc <= i_pc |
+ {{(AW-17){iword[17]}},iword[16:0]} |
+ {{(AW-1){1'b0}},1'b1}; |
end |
|
assign w_ljmp_dly = r_ljmp; |
assign o_early_branch = r_early_branch; |
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_ljmp = 1'b0; |
end endgenerate |
|
|
// To be a pipeable operation there must be ... |
// 1. Two valid adjacent instructions |
// 2. Both must be memory operations, of the same time (both lods |
// or both stos) |
// 3. Both must use the same register base address |
// 4. Both must be to the same address, or the address incremented |
// by one |
// 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)&&(~i_instruction[31]) |
&&(w_dcdM)&&(o_M)&&(o_op[0] ==i_instruction[22]) |
&&(i_instruction[17:14] == o_dcdB[3:0]) |
&&(i_instruction[17:14] != o_dcdA[3:0]) |
&&(i_gie == o_gie) |
&&((i_instruction[21:19]==o_cond[2:0]) |
||(o_cond[2:0] == 3'h0)) |
&&((i_instruction[13:0]==r_I[13:0]) |
||({1'b0, i_instruction[13:0]}==(r_I[13:0]+14'h1))); |
assign o_pipe = r_pipe; |
`else |
assign o_pipe = 1'b0; |
`endif |
|
always @(posedge i_clk) |
if (i_rst) |
r_valid <= 1'b0; |
else if ((i_ce)&&(o_ljmp)) |
r_valid <= 1'b0; |
else if ((i_ce)&&(i_pf_valid)) |
r_valid <= 1'b1; |
else if (~i_stalled) |
r_valid <= 1'b0; |
|
|
assign o_I = { {(32-22){r_I[22]}}, r_I[21:0] }; |
|
endmodule |
/rtl/cpu/wbpriarbiter.v
0,0 → 1,117
/////////////////////////////////////////////////////////////////////////// |
// |
// 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, 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
module wbpriarbiter(i_clk, |
// Bus A |
i_a_cyc, i_a_stb, i_a_we, i_a_adr, i_a_dat, 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, o_b_ack, o_b_stall, o_b_err, |
// Both buses |
o_cyc, o_stb, o_we, o_adr, o_dat, i_ack, i_stall, i_err); |
parameter DW=32, AW=32; |
// |
input i_clk; |
// Bus A |
input i_a_cyc, i_a_stb, i_a_we; |
input [(AW-1):0] i_a_adr; |
input [(DW-1):0] i_a_dat; |
output wire o_a_ack, o_a_stall, o_a_err; |
// Bus B |
input i_b_cyc, i_b_stb, i_b_we; |
input [(AW-1):0] i_b_adr; |
input [(DW-1):0] i_b_dat; |
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; |
input 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) |
assign o_cyc = (r_a_owner) ? i_a_cyc : i_b_cyc; |
reg r_a_owner; |
initial r_a_owner = 1'b1; |
always @(posedge i_clk) |
if (~i_b_cyc) |
r_a_owner <= 1'b1; |
else if ((i_b_cyc)&&(~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 the various lines are |
// irrelevant. |
assign o_stb = (r_a_owner) ? i_a_stb : i_b_stb; |
assign o_we = (r_a_owner) ? i_a_we : i_b_we; |
assign o_adr = (r_a_owner) ? i_a_adr : i_b_adr; |
assign o_dat = (r_a_owner) ? i_a_dat : i_b_dat; |
|
// 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; |
|
endmodule |
|
/rtl/cpu/wbdmac.v
0,0 → 1,471
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
// Filename: wbdmac.v |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// Purpose: Wishbone DMA controller |
// |
// This module is controllable via the wishbone, and moves values from |
// one location in the wishbone address space to another. The amount of |
// memory moved at any given time can be up to 4kB, or equivalently 1kW. |
// Four registers control this DMA controller: a control/status register, |
// a length register, a source WB address and a destination WB address. |
// These register may be read at any time, but they may only be written |
// to when the controller is idle. |
// |
// The meanings of three of the setup registers should be self explanatory: |
// - The length register controls the total number of words to |
// transfer. |
// - The source address register controls where the DMA controller |
// reads from. This address may or may not be incremented |
// after each read, depending upon the setting in the |
// control/status register. |
// - The destination address register, which controls where the DMA |
// controller writes to. This address may or may not be |
// incremented after each write, also depending upon the |
// setting in the control/status register. |
// |
// It is the control/status register, at local address zero, that needs |
// more definition: |
// |
// Bits: |
// 31 R Write protect If this is set to one, it means the |
// write protect bit is set and the controller |
// is therefore idle. This bit will be set upon |
// completing any transfer. |
// 30 R Error. The controller stopped mid-transfer |
// after receiving a bus error. |
// 29 R/W inc_s_n If set to one, the source address |
// will not increment from one read to the next. |
// 28 R/W inc_d_n If set to one, the destination address |
// will not increment from one write to the next. |
// 27 R Always 0 |
// 26..16 R nread Indicates how many words have been read, |
// and not necessarily written (yet). This |
// combined with the cfg_len parameter should tell |
// exactly where the controller is at mid-transfer. |
// 27..16 W WriteProtect When a 12'h3db is written to these |
// bits, the write protect bit will be cleared. |
// |
// 15 R/W on_dev_trigger When set to '1', the controller will |
// wait for an external interrupt before starting. |
// 14..10 R/W device_id This determines which external interrupt |
// will trigger a transfer. |
// 9..0 R/W transfer_len How many bytes to transfer at one time. |
// The minimum transfer length is one, while zero |
// is mapped to a transfer length of 1kW. |
// |
// |
// 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) |
// 2. Write values into length, source and destination address. |
// (writei(3, &vals) should be sufficient for this.) |
// 3. Enable the DMAC interrupt in whatever interrupt controller is present |
// on the system. |
// 4. Write the final start command to the setup/control/status register: |
// Set inc_s_n, inc_d_n, on_dev_trigger, dev_trigger, |
// appropriately for your task |
// Write 12'h3db to the upper word. |
// Set the lower word to either all zeros, or a smaller transfer |
// length if desired. |
// 5. wait() for the interrupt and the operation to complete. |
// Prior to completion, number of items successfully transferred |
// be read from the length register. If the internal buffer is |
// being used, then you can read how much has been read into that |
// buffer by reading from bits 25..16 of this control/status |
// register. |
// |
// Creator: Dan Gisselquist |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// |
`define DMA_IDLE 3'b000 |
`define DMA_WAIT 3'b001 |
`define DMA_READ_REQ 3'b010 |
`define DMA_READ_ACK 3'b011 |
`define DMA_PRE_WRITE 3'b100 |
`define DMA_WRITE_REQ 3'b101 |
`define DMA_WRITE_ACK 3'b110 |
|
module wbdmac(i_clk, i_rst, |
i_swb_cyc, i_swb_stb, i_swb_we, i_swb_addr, i_swb_data, |
o_swb_ack, o_swb_stall, o_swb_data, |
o_mwb_cyc, o_mwb_stb, o_mwb_we, o_mwb_addr, o_mwb_data, |
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; |
// 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; |
// Slave/control wishbone outputs |
output reg o_swb_ack; |
output wire o_swb_stall; |
output reg [(DW-1):0] o_swb_data; |
// Master/DMA wishbone control |
output wire o_mwb_cyc, o_mwb_stb, o_mwb_we; |
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; |
// The interrupt device interrupt lines |
input [(DW-1):0] i_dev_ints; |
// An interrupt to be set upon completion |
output reg o_interrupt; |
// Need to release the bus for a higher priority user |
// This logic had lots of problems, so it is being |
// removed. If you want to make sure the bus is available |
// for a higher priority user, adjust the transfer length |
// accordingly. |
// |
// input i_other_busmaster_requests_bus; |
// |
|
|
reg [2:0] dma_state; |
reg cfg_err, cfg_len_nonzero; |
reg [(AW-1):0] cfg_waddr, cfg_raddr, cfg_len; |
reg [(LGMEMLEN-1):0] cfg_blocklen_sub_one; |
reg cfg_incs, cfg_incd; |
reg [(LGDV-1):0] cfg_dev_trigger; |
reg cfg_on_dev_trigger; |
|
// Single block operations: We'll read, then write, up to a single |
// memory block here. |
|
reg [(DW-1):0] dma_mem [0:(((1<<LGMEMLEN))-1)]; |
reg [(LGMEMLEN):0] nread, nwritten, nwacks, nracks; |
wire [(AW-1):0] bus_nracks; |
assign bus_nracks = { {(AW-LGMEMLEN-1){1'b0}}, nracks }; |
|
reg last_read_request, last_read_ack, |
last_write_request, last_write_ack; |
reg trigger, abort; |
|
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) |
`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_cyc)&&(i_swb_stb)&&(i_swb_we)) |
begin |
case(i_swb_addr) |
2'b00: begin |
if ((i_swb_data[27:16] == 12'hfed) |
&&(cfg_len_nonzero)) |
dma_state <= `DMA_WAIT; |
cfg_blocklen_sub_one |
<= i_swb_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]; |
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]; |
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 (trigger) |
dma_state <= `DMA_READ_REQ; |
end |
`DMA_READ_REQ: begin |
nwritten <= 0; |
|
if (~i_mwb_stall) |
begin |
// Number of read acknowledgements needed |
nracks <= nracks+1; |
if (last_read_request) |
//((nracks == {1'b0, cfg_blocklen_sub_one})||(bus_nracks == cfg_len-1)) |
// Wishbone interruptus |
dma_state <= `DMA_READ_ACK; |
if (cfg_incs) |
o_mwb_addr <= o_mwb_addr |
+ {{(AW-1){1'b0}},1'b1}; |
end |
|
if (i_mwb_err) |
begin |
cfg_len <= 0; |
dma_state <= `DMA_IDLE; |
end |
if (abort) |
dma_state <= `DMA_IDLE; |
if (i_mwb_ack) |
begin |
nread <= nread+1; |
if (cfg_incs) |
cfg_raddr <= cfg_raddr |
+ {{(AW-1){1'b0}},1'b1}; |
end end |
`DMA_READ_ACK: begin |
nwritten <= 0; |
|
if (i_mwb_err) |
begin |
cfg_len <= 0; |
dma_state <= `DMA_IDLE; |
end else if (i_mwb_ack) |
begin |
nread <= nread+1; |
if (last_read_ack) // (nread+1 == nracks) |
dma_state <= `DMA_PRE_WRITE; |
if (cfg_incs) |
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; |
dma_state <= (abort)?`DMA_IDLE:`DMA_WRITE_REQ; |
end |
`DMA_WRITE_REQ: begin |
if (~i_mwb_stall) |
begin |
nwritten <= nwritten+1; |
if (last_write_request) // (nwritten == nread-1) |
// Wishbone interruptus |
dma_state <= `DMA_WRITE_ACK; |
if (cfg_incd) |
begin |
o_mwb_addr <= o_mwb_addr |
+ {{(AW-1){1'b0}},1'b1}; |
cfg_waddr <= cfg_waddr |
+ {{(AW-1){1'b0}},1'b1}; |
end |
end |
|
if (i_mwb_err) |
begin |
cfg_len <= 0; |
dma_state <= `DMA_IDLE; |
end |
if (i_mwb_ack) |
begin |
nwacks <= nwacks+1; |
cfg_len <= cfg_len +{(AW){1'b1}}; // -1 |
end |
if (abort) |
dma_state <= `DMA_IDLE; |
end |
`DMA_WRITE_ACK: begin |
if (i_mwb_err) |
begin |
cfg_len <= 0; |
nread <= 0; |
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) |
dma_state <= `DMA_IDLE; |
end |
default: |
dma_state <= `DMA_IDLE; |
endcase |
|
initial o_interrupt = 1'b0; |
always @(posedge i_clk) |
o_interrupt <= (dma_state == `DMA_WRITE_ACK)&&(i_mwb_ack) |
&&(last_write_ack) |
&&(cfg_len == {{(AW-1){1'b0}},1'b1}); |
|
initial cfg_err = 1'b0; |
always @(posedge i_clk) |
if (dma_state == `DMA_IDLE) |
begin |
if ((i_swb_cyc)&&(i_swb_stb)&&(i_swb_we) |
&&(i_swb_addr==2'b00)) |
cfg_err <= 1'b0; |
end else if (((i_mwb_err)&&(o_mwb_cyc))||(abort)) |
cfg_err <= 1'b1; |
|
initial last_read_request = 1'b0; |
always @(posedge i_clk) |
if ((dma_state == `DMA_WAIT)||(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); |
end else |
last_read_request <= 1'b0; |
|
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) |
last_read_ack <= (nread+2 == nracks); |
else |
last_read_ack <= (nread+1 == nracks); |
end else |
last_read_ack <= 1'b0; |
|
initial last_write_request = 1'b0; |
always @(posedge i_clk) |
if (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) |
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) |
||(dma_state == `DMA_WRITE_ACK); |
|
assign o_mwb_stb = (dma_state == `DMA_READ_REQ) |
||(dma_state == `DMA_WRITE_REQ); |
|
assign o_mwb_we = (dma_state == `DMA_PRE_WRITE) |
||(dma_state == `DMA_WRITE_REQ) |
||(dma_state == `DMA_WRITE_ACK); |
|
// |
// This is tricky. In order for Vivado to consider dma_mem to be a |
// proper memory, it must have a simple address fed into it. Hence |
// the read_address (rdaddr) register. The problem is that this |
// register must always be one greater than the address we actually |
// want to read from, unless we are idling. So ... the math is touchy. |
// |
reg [(LGMEMLEN-1):0] rdaddr; |
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}; |
always @(posedge i_clk) |
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) |
2'b00: o_swb_data <= { (dma_state != `DMA_IDLE), cfg_err, |
~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 |
|
// 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 |
// develop the trigger. |
initial trigger = 1'b0; |
always @(posedge i_clk) |
trigger <= (dma_state == `DMA_WAIT) |
&&((~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. |
always @(posedge i_clk) |
o_swb_ack <= (i_swb_cyc)&&(i_swb_stb); |
|
assign o_swb_stall = 1'b0; |
|
initial abort = 1'b0; |
always @(posedge i_clk) |
abort <= (i_rst)||((i_swb_cyc)&&(i_swb_stb)&&(i_swb_we) |
&&(i_swb_addr == 2'b00) |
&&(i_swb_data == 32'hffed0000)); |
|
endmodule |
|
/rtl/cpu/memops.v
0,0 → 1,150
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: memops.v |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// Purpose: A memory unit to support a CPU. |
// |
// 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. |
// |
// 20150919 -- Added support for handling BUS ERR's (i.e., the WB |
// error signal). |
// |
// 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. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
module memops(i_clk, i_rst, i_stb, i_lock, |
i_op, i_addr, i_data, i_oreg, |
o_busy, o_valid, o_err, o_wreg, o_result, |
o_wb_cyc_gbl, o_wb_cyc_lcl, |
o_wb_stb_gbl, o_wb_stb_lcl, |
o_wb_we, o_wb_addr, o_wb_data, |
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data); |
parameter ADDRESS_WIDTH=24, IMPLEMENT_LOCK=0, AW=ADDRESS_WIDTH; |
input i_clk, i_rst; |
input i_stb, i_lock; |
// CPU interface |
input i_op; |
input [31:0] i_addr; |
input [31:0] i_data; |
input [4:0] i_oreg; |
// CPU outputs |
output wire o_busy; |
output reg o_valid; |
output reg o_err; |
output reg [4:0] o_wreg; |
output reg [31:0] o_result; |
// Wishbone outputs |
output wire o_wb_cyc_gbl; |
output reg o_wb_stb_gbl; |
output wire o_wb_cyc_lcl; |
output reg o_wb_stb_lcl; |
output reg o_wb_we; |
output reg [(AW-1):0] o_wb_addr; |
output reg [31:0] o_wb_data; |
// Wishbone inputs |
input i_wb_ack, i_wb_stall, i_wb_err; |
input [31:0] i_wb_data; |
|
reg r_wb_cyc_gbl, r_wb_cyc_lcl; |
wire gbl_stb, lcl_stb; |
assign lcl_stb = (i_stb)&&(i_addr[31:8]==24'hc00000)&&(i_addr[7:5]==3'h0); |
assign gbl_stb = (i_stb)&&((i_addr[31:8]!=24'hc00000)||(i_addr[7:5]!=3'h0)); |
|
initial r_wb_cyc_gbl = 1'b0; |
initial r_wb_cyc_lcl = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
begin |
r_wb_cyc_gbl <= 1'b0; |
r_wb_cyc_lcl <= 1'b0; |
end else if ((r_wb_cyc_gbl)||(r_wb_cyc_lcl)) |
begin |
if ((i_wb_ack)||(i_wb_err)) |
begin |
r_wb_cyc_gbl <= 1'b0; |
r_wb_cyc_lcl <= 1'b0; |
end |
end else if (i_stb) // New memory operation |
begin // Grab the wishbone |
r_wb_cyc_lcl <= lcl_stb; |
r_wb_cyc_gbl <= gbl_stb; |
end |
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 |
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 |
always @(posedge i_clk) |
if (i_stb) |
begin |
o_wb_we <= i_op; |
o_wb_data <= i_data; |
o_wb_addr <= i_addr[(AW-1):0]; |
end |
|
initial o_valid = 1'b0; |
always @(posedge i_clk) |
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 <= ((o_wb_cyc_gbl)||(o_wb_cyc_lcl))&&(i_wb_err); |
assign o_busy = (o_wb_cyc_gbl)||(o_wb_cyc_lcl); |
|
always @(posedge i_clk) |
if (i_stb) |
o_wreg <= i_oreg; |
always @(posedge i_clk) |
if (i_wb_ack) |
o_result <= i_wb_data; |
|
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) |
begin |
lock_gbl <= (i_lock)&&((r_wb_cyc_gbl)||(lock_gbl)); |
lock_lcl <= (i_lock)&&((r_wb_cyc_lcl)||(lock_lcl)); |
end |
|
assign o_wb_cyc_gbl = (r_wb_cyc_gbl)||(lock_gbl); |
assign o_wb_cyc_lcl = (r_wb_cyc_lcl)||(lock_lcl); |
end else begin |
assign o_wb_cyc_gbl = (r_wb_cyc_gbl); |
assign o_wb_cyc_lcl = (r_wb_cyc_lcl); |
end endgenerate |
endmodule |
/rtl/cpu/prefetch.v
0,0 → 1,124
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: prefetch.v |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// 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. |
// |
// 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. |
// |
// 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. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// 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_ce, i_stalled_n, i_pc, i_aux, |
o_i, o_pc, o_aux, 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, AUX_WIDTH = 1, AW=ADDRESS_WIDTH; |
input i_clk, i_rst, i_ce, i_stalled_n; |
input [(AW-1):0] i_pc; |
input [(AUX_WIDTH-1):0] i_aux; |
output reg [31:0] o_i; |
output reg [(AW-1):0] o_pc; |
output reg [(AUX_WIDTH-1):0] o_aux; |
output reg o_valid, o_illegal; |
// 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; |
// And return inputs |
input i_wb_ack, i_wb_stall, i_wb_err; |
input [31:0] i_wb_data; |
|
assign o_wb_we = 1'b0; |
assign o_wb_data = 32'h0000; |
|
// Let's build it simple and upgrade later: For each instruction |
// we do one bus cycle to get the instruction. Later we should |
// 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)) |
begin |
o_wb_cyc <= 1'b0; |
o_wb_stb <= 1'b0; |
end else if ((i_ce)&&(~o_wb_cyc)) // Initiate a bus cycle |
begin |
o_wb_cyc <= 1'b1; |
o_wb_stb <= 1'b1; |
end else if (o_wb_cyc) // Independent of ce |
begin |
if ((o_wb_cyc)&&(o_wb_stb)&&(~i_wb_stall)) |
o_wb_stb <= 1'b0; |
if (i_wb_ack) |
o_wb_cyc <= 1'b0; |
end |
|
always @(posedge i_clk) |
if (i_rst) // Set the address to guarantee the result is invalid |
o_wb_addr <= {(AW){1'b1}}; |
else if ((i_ce)&&(~o_wb_cyc)) |
o_wb_addr <= i_pc; |
always @(posedge i_clk) |
if ((o_wb_cyc)&&(i_wb_ack)) |
o_aux <= i_aux; |
always @(posedge i_clk) |
if ((o_wb_cyc)&&(i_wb_ack)) |
o_i <= i_wb_data; |
always @(posedge i_clk) |
if ((o_wb_cyc)&&(i_wb_ack)) |
o_pc <= o_wb_addr; |
initial o_valid = 1'b0; |
initial o_illegal = 1'b0; |
always @(posedge i_clk) |
if ((o_wb_cyc)&&(i_wb_ack)) |
begin |
o_valid <= (i_pc == o_wb_addr)&&(~i_wb_err); |
o_illegal <= i_wb_err; |
end else if (i_stalled_n) |
begin |
o_valid <= 1'b0; |
o_illegal <= 1'b0; |
end |
|
endmodule |
/rtl/cpu/zipcounter.v
0,0 → 1,78
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: zipcounter.v |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// Purpose: |
// A very, _very_ simple counter. It's purpose doesn't really |
// include rollover, but it will interrupt on rollover. It can be set, |
// although my design concept is that it can be reset. It cannot be |
// halted. It will always produce interrupts--whether or not they are |
// handled interrupts is another question--that's up to the interrupt |
// controller. |
// |
// My intention is to use this counter for process accounting: I should |
// be able to use this to count clock ticks of processor time assigned to |
// each task by resetting the counter at the beginning of every task |
// interval, and reading the result at the end of the interval. As long |
// as the interval is less than 2^32 clocks, there should be no problem. |
// Similarly, this can be used to measure CPU wishbone bus stalls, |
// prefetch stalls, or other CPU stalls (i.e. stalling as part of a JMP |
// instruction, or a read from the condition codes following a write). |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
module zipcounter(i_clk, i_ce, |
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_data, |
o_wb_ack, o_wb_stall, o_wb_data, |
o_int); |
parameter BW = 32; |
input i_clk, i_ce; |
// Wishbone inputs |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [(BW-1):0] i_wb_data; |
// Wishbone outputs |
output reg o_wb_ack; |
output wire o_wb_stall; |
output reg [(BW-1):0] o_wb_data; |
// Interrupt line |
output reg o_int; |
|
initial o_int = 0; |
initial o_wb_data = 32'h00; |
always @(posedge i_clk) |
if ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_we)) |
{ o_int, o_wb_data } <= { 1'b0, i_wb_data }; |
else if (i_ce) |
{ o_int, o_wb_data } <= o_wb_data+{{(BW-1){1'b0}},1'b1}; |
else |
o_int <= 1'b0; |
|
initial o_wb_ack = 1'b0; |
always @(posedge i_clk) |
o_wb_ack <= (i_wb_cyc)&&(i_wb_stb); |
assign o_wb_stall = 1'b0; |
endmodule |
/rtl/cpu/zipjiffies.v
0,0 → 1,144
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: zipjiffies.v |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// Purpose: This peripheral is motivated by the Linux use of 'jiffies'. |
// A process, in Linux, can request to be put to sleep until a certain |
// number of 'jiffies' have elapsed. Using this interface, the CPU can |
// read the number of 'jiffies' from this peripheral (it only has the |
// one location in address space), add the sleep length to it, and |
// write the result back to the peripheral. The zipjiffies peripheral |
// will record the value written to it only if it is nearer the current |
// counter value than the last current waiting interrupt time. If no |
// other interrupts are waiting, and this time is in the future, it will |
// be enabled. (There is currrently no way to disable a jiffie interrupt |
// once set.) The processor may then place this sleep request into a |
// list among other sleep requests. Once the timer expires, it would |
// write the next jiffy request to the peripheral and wake up the process |
// whose timer had expired. |
// |
// Quite elementary, really. |
// |
// Interface: |
// This peripheral contains one register: a counter. Reads from the |
// register return the current value of the counter. Writes within |
// the (N-1) bit space following the current time set an interrupt. |
// Writes of values that occurred in the last 2^(N-1) ticks will be |
// ignored. The timer then interrupts when it's value equals that time. |
// Multiple writes cause the jiffies timer to select the nearest possible |
// interrupt. Upon an interrupt, the next interrupt time/value is cleared |
// and will need to be reset if the CPU wants to get notified again. With |
// only the single interface, there is no way of knowing when the next |
// interrupt is scheduled for, neither is there any way to slow down the |
// interrupt timer in case you don't want it overflowing as often and you |
// wish to wait more jiffies than it supports. Thus, currently, if you |
// have a timer you wish to wait upon that is more than 2^31 into the |
// future, you would need to set timers along the way, wake up on those |
// timers, and set further timer's until you finally get to your |
// destination. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
module zipjiffies(i_clk, i_ce, |
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_data, |
o_wb_ack, o_wb_stall, o_wb_data, |
o_int); |
parameter BW = 32; |
input i_clk, i_ce; |
// Wishbone inputs |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [(BW-1):0] i_wb_data; |
// Wishbone outputs |
output reg o_wb_ack; |
output wire o_wb_stall; |
output wire [(BW-1):0] o_wb_data; |
// Interrupt line |
output reg o_int; |
|
// |
// Our counter logic: The counter is always counting up--it cannot |
// be stopped or altered. It's really quite simple. Okay, not quite. |
// We still support the clock enable line. We do this in order to |
// support debugging, so that if we get everything running inside a |
// debugger, the timer's all slow down so that everything can be stepped |
// together, one clock at a time. |
// |
reg [(BW-1):0] r_counter; |
always @(posedge i_clk) |
if (i_ce) |
r_counter <= r_counter+1; |
|
// |
// Writes to the counter set an interrupt--but only if they are in the |
// future as determined by the signed result of an unsigned subtract. |
// |
reg int_set, new_set; |
reg [(BW-1):0] int_when, new_when; |
wire signed [(BW-1):0] till_when, till_wb; |
assign till_when = int_when-r_counter; |
assign till_wb = new_when-r_counter; |
|
initial new_set = 1'b0; |
always @(posedge i_clk) |
begin |
// Delay things by a clock to simplify our logic |
new_set <= ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_we)); |
// new_when is a don't care when new_set = 0, so don't worry |
// about setting it at all times. |
new_when<= i_wb_data; |
end |
|
initial o_int = 1'b0; |
initial int_set = 1'b0; |
always @(posedge i_clk) |
begin |
o_int <= 1'b0; |
if ((i_ce)&&(int_set)&&(r_counter == int_when)) |
// Interrupts are self-clearing |
o_int <= 1'b1; // Set the interrupt flag for one clock |
else if ((new_set)&&(till_wb <= 0)) |
o_int <= 1'b1; |
|
if ((new_set)&&(till_wb > 0)) |
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 |
|
// |
// Acknowledge any wishbone accesses -- everything we did took only |
// one clock anyway. |
// |
always @(posedge i_clk) |
o_wb_ack <= (i_wb_cyc)&&(i_wb_stb); |
|
assign o_wb_data = r_counter; |
assign o_wb_stall = 1'b0; |
endmodule |
/rtl/cpu/ziptimer.v
0,0 → 1,158
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: ziptimer.v |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// Purpose: A lighter weight implementation of the Zip Timer. |
// |
// Interface: |
// Two options: |
// 1. One combined register for both control and value, and ... |
// The reload value is set any time the timer data value is "set". |
// Reading the register returns the timer value. Controls are |
// set so that writing a value to the timer automatically starts |
// it counting down. |
// 2. Two registers, one for control one for value. |
// The control register would have the reload value in it. |
// On the clock when the interface is set to zero the interrupt is set. |
// Hence setting the timer to zero will disable the timer without |
// setting any interrupts. Thus setting it to five will count |
// 5 clocks: 5, 4, 3, 2, 1, Interrupt. |
// |
// |
// Control bits: |
// (Start_n/Stop. This bit has been dropped. Writing to this |
// timer any value but zero starts it. Writing a zero |
// clears and stops it.) |
// AutoReload. If set, then on reset the timer automatically |
// loads the last set value and starts over. This is |
// useful for distinguishing between a one-time interrupt |
// timer, and a repetitive interval timer. |
// (INTEN. Interrupt enable--reaching zero always creates an |
// interrupt, so this control bit isn't needed. The |
// interrupt controller can be used to mask the interrupt.) |
// (COUNT-DOWN/UP: This timer is *only* a count-down timer. |
// There is no means of setting it to count up.) |
// WatchDog |
// This timer can be implemented as a watchdog timer simply by |
// connecting the interrupt line to the reset line of the CPU. |
// When the timer then expires, it will trigger a CPU reset. |
// |
// |
// 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. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
module ziptimer(i_clk, i_rst, i_ce, |
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_data, |
o_wb_ack, o_wb_stall, o_wb_data, |
o_int); |
parameter BW = 32, VW = (BW-1), RELOADABLE=1; |
input i_clk, i_rst, i_ce; |
// Wishbone inputs |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [(BW-1):0] i_wb_data; |
// Wishbone outputs |
output reg o_wb_ack; |
output wire o_wb_stall; |
output wire [(BW-1):0] o_wb_data; |
// Interrupt line |
output reg o_int; |
|
reg r_running; |
|
wire wb_write; |
assign wb_write = ((i_wb_cyc)&&(i_wb_stb)&&(i_wb_we)); |
|
wire auto_reload; |
wire [(VW-1):0] reload_value; |
|
initial r_running = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
r_running <= 1'b0; |
else if (wb_write) |
r_running <= (|i_wb_data[(VW-1):0]); |
else if ((o_int)&&(~auto_reload)) |
r_running <= 1'b0; |
|
generate |
if (RELOADABLE != 0) |
begin |
reg r_auto_reload; |
reg [(VW-1):0] r_reload_value; |
|
initial r_auto_reload = 1'b0; |
|
always @(posedge i_clk) |
if (wb_write) |
r_auto_reload <= (i_wb_data[(BW-1)]); |
|
assign auto_reload = r_auto_reload; |
|
// 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; |
end else begin |
assign auto_reload = 1'b0; |
assign reload_value = 0; |
end endgenerate |
|
|
reg [(VW-1):0] r_value; |
initial r_value = 0; |
always @(posedge i_clk) |
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; |
|
// 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) |
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; |
|
initial o_wb_ack = 1'b0; |
always @(posedge i_clk) |
o_wb_ack <= (i_wb_cyc)&&(i_wb_stb); |
assign o_wb_stall = 1'b0; |
|
generate |
if (VW < BW-1) |
assign o_wb_data = { auto_reload, {(BW-1-VW){1'b0}}, r_value }; |
else |
assign o_wb_data = { auto_reload, r_value }; |
endgenerate |
|
endmodule |
/rtl/cpu/zipcpuhs.v
0,0 → 1,1614
/////////////////////////////////////////////////////////////////////////////// |
// |
// 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 |
// RISC as you can get, there are only 16 instruction types supported. |
// Please see the accompanying spec.pdf file for a description of these |
// instructions. |
// |
// All instructions are 32-bits wide. All bus accesses, both address and |
// data, are 32-bits over a wishbone bus. |
// |
// The Zip CPU is fully pipelined with the following pipeline stages: |
// |
// 1. Prefetch, returns the instruction from memory. |
// |
// 2. Instruction Decode |
// |
// 3. Read Operands |
// |
// 4. Apply Instruction |
// |
// 4. Write-back Results |
// |
// Further information about the inner workings of this CPU may be |
// found in the spec.pdf file. (The documentation within this file |
// had become out of date and out of sync with the spec.pdf, so look |
// to the spec.pdf for accurate and up to date information.) |
// |
// |
// In general, the pipelining is controlled by three pieces of logic |
// per stage: _ce, _stall, and _valid. _valid means that the stage |
// holds a valid instruction. _ce means that the instruction from the |
// previous stage is to move into this one, and _stall means that the |
// instruction from the previous stage may not move into this one. |
// The difference between these control signals allows individual stages |
// to propagate instructions independently. In general, the logic works |
// as: |
// |
// |
// assign (n)_ce = (n-1)_valid && (~(n)_stall) |
// |
// |
// always @(posedge i_clk) |
// if ((i_rst)||(clear_pipeline)) |
// (n)_valid = 0 |
// else if (n)_ce |
// (n)_valid = 1 |
// else if (n+1)_ce |
// (n)_valid = 0 |
// |
// assign (n)_stall = ( (n-1)_valid && ( pipeline hazard detection ) ) |
// || ( (n)_valid && (n+1)_stall ); |
// |
// and ... |
// |
// always @(posedge i_clk) |
// if (n)_ce |
// (n)_variable = ... whatever logic for this stage |
// |
// 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-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////////// |
// |
// We can either pipeline our fetches, or issue one fetch at a time. Pipelined |
// fetches are more complicated and therefore use more FPGA resources, while |
// single fetches will cause the CPU to stall for about 5 stalls each |
// instruction cycle, effectively reducing the instruction count per clock to |
// about 0.2. However, the area cost may be worth it. Consider: |
// |
// Slice LUTs ZipSystem ZipCPU |
// Single Fetching 2521 1734 |
// Pipelined fetching 2796 2046 |
// |
// |
// |
`define CPU_CC_REG 4'he |
`define CPU_PC_REG 4'hf |
`define CPU_CLRCACHE_BIT 14 // Floating point error flag, set on error |
`define CPU_PHASE_BIT 13 // Floating point error flag, set on error |
`define CPU_FPUERR_BIT 12 // Floating point error flag, set on error |
`define CPU_DIVERR_BIT 11 // Divide error flag, set on divide by zero |
`define CPU_BUSERR_BIT 10 // Bus error flag, set on error |
`define CPU_TRAP_BIT 9 // User TRAP has taken place |
`define CPU_ILL_BIT 8 // Illegal instruction |
`define CPU_BREAK_BIT 7 |
`define CPU_STEP_BIT 6 // Will step one or two (VLIW) instructions |
`define CPU_GIE_BIT 5 |
`define CPU_SLEEP_BIT 4 |
// Compile time defines |
// |
`include "cpudefs.v" |
// |
// |
module zipcpuhs(i_clk, i_rst, i_interrupt, |
// Debug interface |
i_halt, i_clear_pf_cache, i_dbg_reg, i_dbg_we, i_dbg_data, |
o_dbg_stall, o_dbg_reg, o_dbg_cc, |
o_break, |
// CPU interface to the wishbone bus |
o_wb_gbl_cyc, o_wb_gbl_stb, |
o_wb_lcl_cyc, o_wb_lcl_stb, |
o_wb_we, o_wb_addr, o_wb_data, |
i_wb_ack, i_wb_stall, i_wb_data, |
i_wb_err, |
// Accounting/CPU usage interface |
o_op_stall, o_pf_stall, o_i_count |
`ifdef DEBUG_SCOPE |
, o_debug |
`endif |
); |
parameter RESET_ADDRESS=32'h0100000, ADDRESS_WIDTH=24, |
LGICACHE=6; |
`ifdef OPT_MULTIPLY |
parameter IMPLEMENT_MPY = `OPT_MULTIPLY; |
`else |
parameter IMPLEMENT_MPY = 0; |
`endif |
`ifdef OPT_DIVIDE |
parameter IMPLEMENT_DIVIDE = 1; |
`else |
parameter IMPLEMENT_DIVIDE = 0; |
`endif |
`ifdef OPT_IMPLEMENT_FPU |
parameter IMPLEMENT_FPU = 1, |
`else |
parameter IMPLEMENT_FPU = 0, |
`endif |
IMPLEMENT_LOCK=1; |
`ifdef OPT_EARLY_BRANCHING |
parameter EARLY_BRANCHING = 1; |
`else |
parameter EARLY_BRANCHING = 0; |
`endif |
parameter AW=ADDRESS_WIDTH; |
input i_clk, i_rst, 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; |
// Debug interface -- outputs |
output wire o_dbg_stall; |
output reg [31:0] o_dbg_reg; |
output reg [3:0] o_dbg_cc; |
output wire o_break; |
// Wishbone interface -- outputs |
output wire o_wb_gbl_cyc, o_wb_gbl_stb; |
output wire o_wb_lcl_cyc, o_wb_lcl_stb, o_wb_we; |
output wire [(AW-1):0] o_wb_addr; |
output wire [31:0] o_wb_data; |
// Wishbone interface -- inputs |
input i_wb_ack, i_wb_stall; |
input [31:0] i_wb_data; |
input i_wb_err; |
// Accounting outputs ... to help us count stalls and usage |
output wire o_op_stall; |
output wire o_pf_stall; |
output wire o_i_count; |
// |
`ifdef DEBUG_SCOPE |
output reg [31:0] o_debug; |
`endif |
|
|
// Registers |
// |
// The distributed RAM style comment is necessary on the |
// SPARTAN6 with XST to prevent XST from oversimplifying the register |
// set and in the process ruining everything else. It basically |
// optimizes logic away, to where it no longer works. The logic |
// as described herein will work, this just makes sure XST implements |
// that logic. |
// |
(* ram_style = "distributed" *) |
reg [31:0] regset [0:31]; |
|
// Condition codes |
// (BUS, TRAP,ILL,BREAKEN,STEP,GIE,SLEEP ), V, N, C, Z |
reg [3:0] flags, iflags; |
wire [14:0] w_uflags, w_iflags; |
reg trap, break_en, step, gie, sleep, r_halted, |
break_pending; |
wire w_clear_icache; |
`ifdef OPT_ILLEGAL_INSTRUCTION |
reg ill_err_u, ill_err_i; |
`else |
wire ill_err_u, ill_err_i; |
`endif |
reg ubreak; |
reg ibus_err_flag, ubus_err_flag; |
wire idiv_err_flag, udiv_err_flag; |
wire ifpu_err_flag, ufpu_err_flag; |
wire ihalt_phase, uhalt_phase; |
|
// The master chip enable |
wire master_ce; |
|
// |
// |
// PIPELINE STAGE #1 :: Prefetch |
// Variable declarations |
// |
reg [(AW-1):0] pf_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; |
wire [(AW-1):0] pf_addr; |
wire [31:0] pf_data; |
wire [31:0] instruction; |
wire [(AW-1):0] instruction_pc; |
wire pf_valid, instruction_gie, pf_illegal; |
|
// |
// |
// PIPELINE STAGE #2 :: Instruction Decode |
// Variable declarations |
// |
// |
wire op_stall, dcd_ce, dcd_phase; |
wire [3:0] dcdOp; |
wire [4:0] dcd_iA, dcd_iB, dcd_iR; |
wire dcdA_cc, dcdB_cc, dcdA_pc, dcdB_pc, dcdR_cc, dcdR_pc; |
wire [3:0] dcdF; |
wire dcd_wR, dcd_rA, dcd_rB, |
dcdALU, dcdM, dcdDV, dcdFP, |
dcdF_wr, dcd_gie, dcd_break, dcd_lock, |
dcd_pipe, dcd_ljmp; |
reg [1:0] r_dcdvalid; |
wire dcd_valid; |
wire [(AW-1):0] dcd_pc; |
wire [31:0] dcd_I; |
wire dcd_zI; // true if dcdI == 0 |
wire dcdA_stall, dcdB_stall, dcdF_stall; |
|
wire dcd_illegal; |
wire dcd_early_branch; |
wire [(AW-1):0] dcd_branch_pc; |
|
|
// |
// |
// PIPELINE STAGE #3a :: Read Operands |
// Variable declarations |
// |
// |
// |
// Now, let's read our operands |
reg opa_valid, opa_DV, opa_FP, opa_ALU, opa_M, |
opa_rA, opa_rB; |
reg [4:0] alu_reg; |
reg [3:0] opa_opn; |
reg [4:0] opa_R, opa_iA; |
reg [31:0] r_opa_B; |
reg [(AW-1):0] opa_pc; |
wire [31:0] opA_nowait, opa_Bnowait, opa_A, opa_B, opa_I; |
reg opa_wR, opa_ccR, opa_wF, opa_gie; |
wire [13:0] opa_Fl; |
reg [5:0] r_opa_F; |
wire [7:0] opa_F; |
wire opa_ce, opa_phase, opa_pipe; |
// Some pipeline control wires |
reg opa_A_alu, opa_A_mem; |
reg opa_B_alu, opa_B_mem; |
`ifdef OPT_ILLEGAL_INSTRUCTION |
reg opa_illegal; |
`else |
wire opa_illegal; |
assign opa_illegal = 1'b0; |
`endif |
reg opa_break; |
reg opa_lock; |
|
// |
// |
// PIPELINE STAGE #3b :: Read Operands |
// Variable declarations |
// |
// |
// |
// Now, let's read our operands |
reg [3:0] opb_opn; |
reg opb_valid, opb_valid_mem, opb_valid_alu; |
reg opb_valid_div, opb_valid_fpu; |
reg [4:0] opb_R; |
reg [31:0] r_opb_A, r_opb_B; |
reg [(AW-1):0] opb_pc; |
wire [31:0] opb_A_nowait, opb_B_nowait, opb_A, opb_B; |
reg opb_wR, opb_ccR, opb_wF, opb_gie; |
wire [13:0] opb_Fl; |
reg [5:0] r_opb_F; |
wire [7:0] opb_F; |
wire opb_ce, opb_phase, opb_pipe; |
// Some pipeline control wires |
reg opb_A_alu, opb_A_mem; |
reg opb_B_alu, opb_B_mem; |
`ifdef OPT_ILLEGAL_INSTRUCTION |
reg opb_illegal; |
`else |
wire opb_illegal; |
assign opb_illegal = 1'b0; |
`endif |
reg opb_break; |
reg opb_lock; |
|
|
// |
// |
// PIPELINE STAGE #4 :: ALU / Memory / Divide |
// Variable declarations |
// |
// |
reg [(AW-1):0] alu_pc; |
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; |
wire [3:0] alu_flags; |
wire alu_valid, alu_busy; |
wire set_cond; |
reg alu_wr, alF_wr, alu_gie; |
wire alu_illegal_op; |
wire 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 [4:0] mem_wreg; |
|
wire mem_busy, mem_rdbusy; |
wire [(AW-1):0] mem_addr; |
wire [31:0] mem_data, mem_result; |
|
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)&&(opb_valid_div) |
&&(~stage_busy)&&(set_cond); |
|
wire fpu_ce, fpu_error, fpu_busy, fpu_valid; |
wire [31:0] fpu_result; |
wire [3:0] fpu_flags; |
|
assign fpu_ce = (master_ce)&&(~clear_pipeline)&&(opb_valid_fpu) |
&&(~stage_busy)&&(set_cond); |
|
// |
// |
// PIPELINE STAGE #5 :: Write-back |
// Variable declarations |
// |
wire wr_reg_ce, wr_flags_ce, wr_write_pc, wr_write_cc; |
wire [4:0] wr_reg_id; |
wire [31:0] wr_gpreg_vl, wr_spreg_vl; |
wire w_switch_to_interrupt, w_release_from_interrupt; |
reg [(AW-1):0] upc, ipc; |
|
|
|
// |
// MASTER: clock enable. |
// |
assign master_ce = (~i_halt)&&(~o_break)&&(~sleep); |
|
|
// |
// PIPELINE STAGE #1 :: Prefetch |
// Calculate stall conditions |
// |
// These are calculated externally, within the prefetch module. |
// |
|
// |
// PIPELINE STAGE #2 :: Instruction Decode |
// Calculate stall conditions |
assign dcd_ce = ((~dcd_valid)||(~dcd_stalled))&&(~clear_pipeline); |
|
assign dcd_stalled = (dcd_valid)&&(op_stall); |
// |
// PIPELINE STAGE #3 :: Read Operands |
// Calculate stall conditions |
wire op_lock_stall; |
assign op_stall = (opvalid)&&( // 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) |
// |
// Stall if we are going into memory with an operation |
// that cannot be pipelined, and the memory is |
// already busy |
||(mem_stalled) // &&(opvalid_mem) part of mem_stalled |
) |
||(dcd_valid)&&( |
// Stall if we need to wait for an operand A |
// to be ready to read |
(dcdA_stall) |
// Likewise for B, also includes logic |
// regarding immediate offset (register must |
// be in register file if we need to add to |
// an immediate) |
||(dcdB_stall) |
// Or if we need to wait on flags to work on the |
// CC register |
||(dcdF_stall) |
); |
assign opa_ce = ((dcd_valid)||(dcd_illegal))&&(~opa_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 |
// is de-selected |
// 3. Stall if someone on the other end is writing the CC register, |
// since we don't know if it'll put us to sleep or not. |
// 4. Last case: Stall if we would otherwise move a break instruction |
// through the ALU. Break instructions are not allowed through |
// the ALU. |
assign alu_stall = (((~master_ce)||(mem_rdbusy)||(alu_busy))&&(opvalid_alu)) //Case 1&2 |
// Old case #3--this isn't an ALU stall though ... |
||((opvalid_alu)&&(wr_reg_ce)&&(wr_reg_id[4] == op_gie) |
&&(wr_write_cc)) // Case 3 |
||((opvalid)&&(op_lock)&&(op_lock_stall)) |
||((opvalid)&&(op_break)) |
||(div_busy)||(fpu_busy); |
assign alu_ce = (master_ce)&&(stage_ce)&&(opvalid_alu)&&(~clear_pipeline); |
assign stage_ce = (~div_busy)&&(~alu_busy)&&(~mem_rdbusy)&&(~fpu_busy); |
// |
|
// |
// Note: if you change the conditions for mem_ce, you must also change |
// alu_pc_valid. |
// |
assign mem_ce = (master_ce)&&(opvalid_mem)&&(~mem_stalled) |
&&(~clear_pipeline); |
assign mem_stalled = (~master_ce)||(alu_busy)||((opvalid_mem)&&( |
(mem_pipe_stalled) |
||((~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_write_pc)||(wr_write_cc))))); |
|
|
// |
// |
// PIPELINE STAGE #1 :: Prefetch |
// |
// |
fastcache #(LGICACHE, ADDRESS_WIDTH) |
pf(i_clk, i_rst, (new_pc)||((dcd_early_branch)&&(~clear_pipeline)), |
i_clear_pf_cache, |
// dcd_pc, |
~dcd_stalled, |
((dcd_early_branch)&&(~clear_pipeline)) |
? dcd_branch_pc:pf_pc, |
instruction, 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); |
assign instruction_gie = gie; |
|
// |
// The ifastdec decoder takes two clocks to decode an instruction. |
// Therefore, to determine if a decoded instruction is valid, we |
// need to wait two clocks from pf_valid. Hence, we dump this into |
// a pipeline below. |
// |
initial r_dcdvalid = 2'b00; |
always @(posedge i_clk) |
if ((i_rst)||(clear_pipeline)||(w_clear_icache)) |
r_dcdvalid <= 2'b00; |
else if (dcd_ce) |
r_dcdvalid <= { r_dcdvalid[0], pf_valid }; |
else if (opa_ce) |
r_dcdvalid <= 1'b0; |
assign dcd_valid = r_dcdvalid[1]; |
|
ifastdec #(AW, IMPLEMENT_MPY, EARLY_BRANCHING, IMPLEMENT_DIVIDE, |
IMPLEMENT_FPU) |
instruction_decoder(i_clk, (i_rst)||(clear_pipeline), |
dcd_ce, dcd_stalled, instruction, instruction_gie, |
instruction_pc, pf_valid, pf_illegal, dcd_phase, |
dcd_illegal, dcd_pc, dcd_gie, |
{ dcdR_cc, dcdR_pc, dcd_iR }, |
{ dcdA_cc, dcdA_pc, dcd_iA }, |
{ dcdB_cc, dcdB_pc, dcd_iB }, |
dcd_I, dcd_zI, dcdF, dcdF_wr, dcdOp, |
dcdALU, dcdM, dcdDV, dcdFP, dcd_break, dcd_lock, |
dcd_wR,dcd_rA, dcd_rB, |
dcd_early_branch, |
dcd_branch_pc, dcd_ljmp, |
dcd_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 (op_ce) |
r_op_pipe <= dcd_pipe; |
else if (mem_ce) // Clear us any time an op_ is clocked in |
r_op_pipe <= 1'b0; |
assign op_pipe = r_op_pipe; |
|
// |
// |
// PIPELINE STAGE #3 :: Read Operands (Registers) |
// |
// |
assign w_opA = regset[dcd_iA]; |
assign w_opB = regset[dcd_iB]; |
|
wire [8:0] w_cpu_info; |
assign w_cpu_info = { |
`ifdef OPT_ILLEGAL_INSTRUCTION |
1'b1, |
`else |
1'b0, |
`endif |
1'b1, |
`ifdef OPT_DIVIDE |
1'b1, |
`else |
1'b0, |
`endif |
`ifdef OPT_IMPLEMENT_FPU |
1'b1, |
`else |
1'b0, |
`endif |
1'b1, 1'b1, |
`ifdef OPT_EARLY_BRANCHING |
1'b1, |
`else |
1'b0, |
`endif |
1'b1, |
`ifdef OPT_VLIW |
1'b1 |
`else |
1'b0 |
`endif |
}; |
|
wire [31:0] w_pcA_v; |
generate |
if (AW < 32) |
assign w_pcA_v = {{(32-AW){1'b0}}, (dcd_iA[4] == dcd_gie)?dcd_pc:upc }; |
else |
assign w_pcA_v = (dcd_iA[4] == dcd_gie)?dcd_pc:upc; |
endgenerate |
|
reg [4:0] opa_Aid, opa_Bid; |
reg opa_Ard, opa_Brd; |
always @(posedge i_clk) |
if (opa_ce) |
begin |
opa_iA <= dcd_iA; |
opa_iB <= dcd_iB; |
opa_rA <= dcd_rA; |
opa_rB <= dcd_rB; |
end |
|
always @(posedge i_clk) |
if (opa_ce) |
begin |
if ((wr_reg_ce)&&(wr_reg_id == dcd_iA)) |
r_opA <= wr_gpreg_vl; |
else if (dcdA_pc) |
r_opA <= w_pcA_v; |
else if (dcdA_cc) |
r_opA <= { w_cpu_info, w_opA[22:15], (dcd_iA[4])?w_uflags:w_iflags }; |
else |
r_opA <= w_opA; |
end else if ((wr_reg_ce)&&(wr_reg_id == opa_iA)&&(opa_rA)) |
r_opA <= wr_gpreg_vl; |
|
wire [31:0] w_opBnI, w_pcB_v; |
generate |
if (AW < 32) |
assign w_pcB_v = {{(32-AW){1'b0}}, (dcdB[4] == dcd_gie)?dcd_pc:upc }; |
else |
assign w_pcB_v = (dcdB[4] == dcd_gie)?dcd_pc:upc; |
endgenerate |
|
always @(posedge i_clk) |
if (opa_ce) |
begin |
opa_B <= (~dcdB_rd) ? 32'h00 |
: (((wr_reg_ce)&&(wr_reg_id == dcdB)) ? wr_gpreg_vl |
: ((dcdB_pc) ? w_pcB_v |
: ((dcdB_cc) ? { w_cpu_info, w_opB[22:14], // w_opB[31:14], |
(dcdB[4])?w_uflags:w_iflags} |
: w_opB))); |
opa_I <= dcd_I; |
end |
|
// |
// B-Inflight |
// |
// We cannot read the B register if it is "in-flight", that is if the |
// result register of any previous instruction still needs to be written. |
// |
// reg [31:0] opa_b_inflight; |
// always @(posedge i_clk) |
// if ((i_reset)||(clear_pipeline)) |
// opa_b_inflight <= 32'h00; |
// else begin |
// if (wr_reg_ce) |
// opa_b_inflight[wr_reg_id] <= 1'b0; |
// if (opb_ce) |
// opa_b_inflight[opa_Rid] <= 1'b1; |
// end |
// |
// always @(posedge i_clk) |
// if (opa_b_invalid) |
// opa_b_invalid <= opa_b_inflight[opa_A]; |
// else |
// opa_b_invalid <= opa_b_inflight[dcd_iA]; |
// |
|
always @(posedge i_clk) |
if (opb_ce) |
opb_B <= opa_B + opa_I; |
else if ((wr_reg_ce)&&(opa_Bid == wr_reg_id)&&(opa_Brd)) |
opb_B <= wr_gpreg_vl; |
|
always @(posedge i_clk) |
if (opa_ce) |
opa_F <= dcdF; |
always @(posedge i_clk) |
if (opb_ce) |
begin |
case(opa_F[2:0]) |
3'h0: r_opb_F <= 6'h00; // Always |
// These were remapped as part of the new instruction |
// set in order to make certain that the low order |
// two bits contained the most commonly used |
// conditions: Always, LT, Z, and NZ. |
3'h1: r_opb_F <= 6'h24; // LT |
3'h2: r_opb_F <= 6'h11; // Z |
3'h3: r_opb_F <= 6'h10; // NE |
3'h4: r_opb_F <= 6'h30; // GT (!N&!Z) |
3'h5: r_opb_F <= 6'h20; // GE (!N) |
3'h6: r_opb_F <= 6'h02; // C |
3'h7: r_opb_F <= 6'h08; // V |
endcase |
end // Bit order is { (flags_not_used), VNCZ mask, VNCZ value } |
assign opb_F = { r_opb_F[3], r_opb_F[5], r_opb_F[1], r_opb_F[4:0] }; |
|
wire w_opa_valid; |
always @(posedge i_clk) |
if (i_rst) |
opa_valid <= 1'b0; |
else if (opa_ce) |
opa_valid <= ((dcd_valid)||(dcd_illegal))&&(~clear_pipeline); |
|
always @(posedge i_clk) |
if ((i_rst)||(clear_pipeline)) |
begin |
opa_valid <= 1'b0; |
end else if (opa_ce) |
begin |
opa_valid <=(dcd_valid); |
opa_M <= (dcd_valid)&&(opa_M )&&(~opa_illegal); |
opa_DV <= (dcd_valid)&&(opa_DV )&&(~opa_illegal); |
opa_FP <= (dcd_valid)&&(opa_FP )&&(~opa_illegal); |
end else if (opb_ce) |
opa_valid <= 1'b0; |
|
initial opb_valid = 1'b0; |
initial opb_valid_alu = 1'b0; |
initial opb_valid_mem = 1'b0; |
initial opb_valid_div = 1'b0; |
initial opb_valid_fpu = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(clear_pipeline)) |
begin |
opb_valid <= 1'b0; |
opb_valid_alu <= 1'b0; |
opb_valid_mem <= 1'b0; |
opb_valid_div <= 1'b0; |
opb_valid_fpu <= 1'b0; |
end else if (opb_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. |
opb_valid <= (opa_valid); |
opb_valid_alu <=(opa_valid)&&((opa_ALU)||(opa_illegal)); |
opb_valid_mem <= (opa_valid)&&(opa_M )&&(~opa_illegal); |
opb_valid_div <= (opa_valid)&&(opa_DV )&&(~opa_illegal); |
opb_valid_fpu <= (opa_valid)&&(opa_FP )&&(~opa_illegal); |
end else if ((clear_pipeline)||(stage_ce)) |
begin |
opb_valid <= 1'b0; |
opb_valid_alu <= 1'b0; |
opb_valid_mem <= 1'b0; |
opb_valid_div <= 1'b0; |
opb_valid_fpu <= 1'b0; |
end |
|
initial op_break = 1'b0; |
always @(posedge i_clk) |
if (i_rst) opb_break <= 1'b0; |
else if (opb_ce) |
opb_break <= (opa_break)&&((break_en)||(~opa_gie)); |
else if ((clear_pipeline)||(~opb_valid)) |
opb_break <= 1'b0; |
|
reg r_op_lock, r_op_lock_stall; |
|
initial r_op_lock_stall = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
r_op_lock_stall <= 1'b0; |
else |
r_op_lock_stall <= (~opvalid)||(~op_lock) |
||(~dcd_valid)||(~pf_valid); |
|
assign op_lock_stall = r_op_lock_stall; |
|
initial opa_lock = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(clear_pipeline)) |
opa_lock <= 1'b0; |
else if (opa_ce) |
opa_lock <= (dcd_lock)&&(~clear_pipeline); |
initial opb_lock = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(clear_pipeline)) |
opb_lock <= 1'b0; |
else if (opb_ce) |
opb_lock <= (opb_lock)&&(~clear_pipeline); |
|
initial opa_illegal = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(clear_pipeline)) |
opa_illegal <= 1'b0; |
else if(opa_ce) |
opa_illegal <=(dcd_illegal); |
initial opb_illegal = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(clear_pipeline)) |
opb_illegal <= 1'b0; |
else if(opb_ce) |
opb_illegal <=(opa_illegal); |
|
always @(posedge i_clk) |
if (opa_ce) |
begin |
opa_wF <= (dcdF_wr)&&((~dcdR_cc)||(~dcd_wR)) |
&&(~dcd_early_branch)&&(~dcd_illegal); |
opa_wR <= (dcd_wR)&&(~dcd_early_branch)&&(~dcd_illegal); |
end |
always @(posedge i_clk) |
if (opb_ce) |
begin |
opb_wF <= opa_wF; |
opb_wR <= opa_wR; |
end |
|
always @(posedge i_clk) |
if (opa_ce) |
begin |
opa_opn <= dcdOp; // Which ALU operation? |
opa_R <= dcd_iR; |
opa_ccR <= (dcdR_cc)&&(dcd_wR)&&(dcd_iR[4]==dcd_gie); |
opa_gie <= dcd_gie; |
// |
opa_pc <= dcd_valid; |
opa_rA <= dcd_; |
opa_rB <= dcd_; |
end |
always @(posedge i_clk) |
if (opb_ce) |
begin |
opb_opn <= opa_opn; |
opb_R <= opa_R; |
opb_ccR <= opa_ccR; |
opb_gie <= opa_gie; |
// |
opb_pc <= opa_pc; |
end |
assign opb_Fl = (opb_gie)?(w_uflags):(w_iflags); |
|
always @(posedge i_clk) |
if ((i_rst)||(clear_pipeline)) |
opa_phase <= 1'b0; |
else if (opa_ce) |
opa_phase <= dcd_phase; |
|
always @(posedge i_clk) |
if ((i_rst)||(clear_pipeline)) |
opb_phase <= 1'b0; |
else if (opb_ce) |
opb_phase <= opa_phase; |
|
assign opA = r_opA; |
|
assign dcdA_stall = (dcd_rA) // &&(dcdvalid) is checked for elsewhere |
&&((opa_valid)||(mem_rdbusy) |
||(div_busy)||(fpu_busy)) |
&&((opF_wr)&&(dcdA_cc)); |
|
assign dcdB_stall = (dcdB_rd) |
&&((opa_valid)||(mem_rdbusy) |
||(div_busy)||(fpu_busy)||(alu_busy)) |
&&( |
// 1. |
((~dcd_zI)&&( |
((opb_R == dcdB)&&(opb_wR)) |
||((mem_rdbusy)&&(~dcd_pipe)) |
)) |
// 2. |
||((opF_wr)&&(dcdB_cc)) |
); |
assign dcdF_stall = ((~dcdF[3]) |
||((dcd_rA)&&(dcdA_cc)) |
||((dcd_rB)&&(dcdB_cc))) |
&&(opvalid)&&(opb_ccR); |
// |
// |
// PIPELINE STAGE #4 :: Apply Instruction |
// |
// |
fastops fastalu(i_clk, i_rst, alu_ce, |
(opb_valid_alu), opb_opn, opb_A, opb_B, |
alu_result, alu_flags, alu_valid, alu_illegal_op, |
alu_busy); |
|
div thedivide(i_clk, (i_rst)||(clear_pipeline), div_ce, opb_opn[0], |
opb_A, opb_B, div_busy, div_valid, div_error, div_result, |
div_flags); |
|
generate |
if (IMPLEMENT_FPU != 0) |
begin |
// |
// sfpu thefpu(i_clk, i_rst, fpu_ce, |
// opA, opB, fpu_busy, fpu_valid, fpu_err, fpu_result, |
// fpu_flags); |
// |
assign fpu_error = 1'b0; // Must only be true if fpu_valid |
assign fpu_busy = 1'b0; |
assign fpu_valid = 1'b0; |
assign fpu_result= 32'h00; |
assign fpu_flags = 4'h0; |
end else begin |
assign fpu_error = 1'b0; |
assign fpu_busy = 1'b0; |
assign fpu_valid = 1'b0; |
assign fpu_result= 32'h00; |
assign fpu_flags = 4'h0; |
end endgenerate |
|
|
assign set_cond = ((opb_F[7:4]&opb_Fl[3:0])==opb_F[3:0]); |
initial alF_wr = 1'b0; |
initial alu_wr = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
begin |
alu_wr <= 1'b0; |
alF_wr <= 1'b0; |
end else if (alu_ce) |
begin |
// alu_reg <= opR; |
alu_wr <= (opb_wR)&&(set_cond); |
alF_wr <= (opb_wF)&&(set_cond); |
end else if (~alu_busy) begin |
// These are strobe signals, so clear them if not |
// set for any particular clock |
alu_wr <= (i_halt)&&(i_dbg_we); |
alF_wr <= 1'b0; |
end |
|
initial alu_phase = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
alu_phase <= 1'b0; |
else if ((adf_ce_unconditional)||(mem_ce)) |
alu_phase <= opb_phase; |
|
always @(posedge i_clk) |
if (adf_ce_unconditional) |
alu_reg <= opb_R; |
else if ((i_halt)&&(i_dbg_we)) |
alu_reg <= i_dbg_reg; |
|
// |
// 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; |
always @(posedge i_clk) |
dbg_val <= i_dbg_data; |
always @(posedge i_clk) |
if (stage_ce) |
alu_gie <= op_gie; |
always @(posedge i_clk) |
if (stage_ce) |
alu_pc <= opb_pc; |
|
initial alu_illegal = 0; |
always @(posedge i_clk) |
if (clear_pipeline) |
alu_illegal <= 1'b0; |
else if (stage_ce) |
alu_illegal <= opb_illegal; |
|
initial r_alu_pc_valid = 1'b0; |
initial mem_pc_valid = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
r_alu_pc_valid <= 1'b0; |
else if (adf_ce_unconditional)//Includes&&(~alu_clear_pipeline) |
r_alu_pc_valid <= 1'b1; |
else if (((~alu_busy)&&(~div_busy)&&(~fpu_busy))||(clear_pipeline)) |
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) |
mem_pc_valid <= 1'b0; |
else |
mem_pc_valid <= (mem_ce); |
|
wire bus_lock; |
|
reg [1:0] r_bus_lock; |
initial r_bus_lock = 2'b00; |
always @(posedge i_clk) |
if (i_rst) |
r_bus_lock <= 2'b00; |
else if ((opb_ce)&&(opb_lock)) |
r_bus_lock <= 2'b11; |
else if ((|r_bus_lock)&&((~opb_valid_mem)||(~opb_ce))) |
r_bus_lock <= r_bus_lock + 2'b11; // r_bus_lock -= 1 |
assign bus_lock = |r_bus_lock; |
|
pipemem #(AW,IMPLEMENT_LOCK) domem(i_clk, i_rst,(mem_ce)&&(set_cond), bus_lock, |
(opb_opn[0]), opb_B, opb_A, opb_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_ack, mem_stall, mem_err, i_wb_data); |
|
assign mem_rdbusy = ((mem_busy)&&(~mem_we)); |
|
// Either the prefetch or the instruction gets the memory bus, but |
// never both. |
wbdblpriarb #(32,AW) pformem(i_clk, i_rst, |
// Memory access to the arbiter, priority position |
mem_cyc_gbl, mem_cyc_lcl, mem_stb_gbl, mem_stb_lcl, |
mem_we, mem_addr, mem_data, mem_ack, mem_stall, mem_err, |
// Prefetch access to the arbiter |
pf_cyc, 1'b0, pf_stb, 1'b0, pf_we, pf_addr, pf_data, |
pf_ack, pf_stall, pf_err, |
// 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, |
i_wb_ack, i_wb_stall, i_wb_err); |
|
|
|
// |
// |
// |
// |
// |
// |
// |
// |
// PIPELINE STAGE #5 :: Write-back results |
// |
// |
|
// Unlike previous versions of the writeback routine(s), this version |
// requires that everything be registered and clocked as soon as it is |
// valid. So, let's start by clocking in our results. |
reg [4:0] r_wr_reg; |
reg [31:0] r_wr_val; |
reg r_wr_ce, r_wr_err; |
|
// 1. Will we need to write a register? |
always @(posedge i_clk) |
r_wr_ce <= (dbgv)||(mem_valid) |
||((~clear_pipeline)&&(~alu_illegal) |
&&(((alu_wr)&&(alu_valid)) |
||(div_valid)||(fpu_valid))); |
assign wr_reg_ce = r_wr_ce; |
|
// 2. Did the ALU/MEM/DIV/FPU stage produce an error of any type? |
// a. Illegal instruction |
// b. Division by zero |
// c. Floating point error |
// d. Bus Error |
// these will be causes for an interrupt on the next clock after this |
// one. |
always @(posedge i_clk) |
r_wr_err <= ((div_valid)&&(div_error)) |
||((fpu_valid)&&(fpu_error)) |
||((alu_pc_valid)&&(alu_illegal)) |
||(bus_err); |
reg r_wr_illegal; |
always @(posedge i_clk) |
r_wr_illegal <= (alu_pc_valid)&&(alu_illegal); |
|
// Which register shall be written? |
// Note that the alu_reg is the register to write on a divide or |
// FPU operation. |
always @(posedge i_clk) |
r_wr_reg <= (alu_wr|div_valid|fpu_valid)?alu_reg:mem_wreg; |
assign wr_reg_id = r_wr_reg; |
|
// 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}); |
assign wr_write_ucc = (wr_reg_id[4:0] == {1'b1, `CPU_CC_REG}); |
// Are we writing to the PC? |
assign wr_write_pc = (wr_reg_id[3:0] == `CPU_PC_REG); |
|
// What value to write? |
always @(posedge i_clk) |
r_wr_val <= ((mem_valid) ? mem_result |
:((div_valid|fpu_valid)) |
? ((div_valid) ? div_result:fpu_result) |
:((dbgv) ? dbg_val : alu_result)); |
assign wr_gpreg_vl = r_wr_val; |
assign wr_spreg_vl = r_wr_val; |
|
// Do we write back our flags? |
reg r_wr_flags_ce; |
initial r_wr_flags_ce = 1'b0; |
always @(posedge i_clk) |
r_wr_flags_ce <= ((alF_wr)||(div_valid)||(fpu_valid)) |
&&(~clear_pipeline)&&(~alu_illegal); |
assign wr_flags_ce = r_wr_flags_ce; |
|
reg [3:0] r_wr_newflags; |
always @(posedge i_clk) |
if (div_valid) |
r_wr_newflags <= div_flags; |
else if (fpu_valid) |
r_wr_newflags <= fpu_flags; |
else // if (alu_valid) |
r_wr_newflags <= alu_flags; |
|
reg r_wr_gie; |
always @(posedge i_clk) |
r_wr_gie <= (~dbgv)&&(alu_gie); |
|
reg r_wr_pc_valid; |
initial r_wr_pc_valid = 1'b0; |
always @(posedge i_clk) |
r_wr_pc_valid <= ((alu_pc_valid)&&(~clear_pipeline)) |
||(mem_pc_valid); |
reg [(AW-1):0] r_wr_pc; |
always @(posedge i_clk) |
r_wr_pc <= alu_pc; // (alu_pc_valid)?alu_pc : mem_pc; |
|
//// |
// |
// |
// Write back, second clock |
// |
// |
//// |
always @(posedge i_clk) |
if (wr_reg_ce) |
regset[wr_reg_id] <= wr_gpreg_vl; |
|
|
assign w_uflags = { uhalt_phase, ufpu_err_flag, |
udiv_err_flag, ubus_err_flag, trap, ill_err_u, |
1'b0, step, 1'b1, sleep, |
((wr_flags_ce)&&(alu_gie))?r_wr_newflags:flags }; |
assign w_iflags = { ihalt_phase, ifpu_err_flag, |
idiv_err_flag, ibus_err_flag, trap, ill_err_i, |
break_en, 1'b0, 1'b0, sleep, |
((wr_flags_ce)&&(~alu_gie))?r_wr_newflags:iflags }; |
|
|
// What value to write? |
always @(posedge i_clk) |
// If explicitly writing the register itself |
if ((wr_reg_ce)&&(wr_reg_id[4])&&(wr_write_cc)) |
flags <= wr_gpreg_vl[3:0]; |
// Otherwise if we're setting the flags from an ALU operation |
else if ((wr_flags_ce)&&(alu_gie)) |
flags <= r_wr_newflags; |
|
always @(posedge i_clk) |
if ((wr_reg_ce)&&(~wr_reg_id[4])&&(wr_write_cc)) |
iflags <= wr_gpreg_vl[3:0]; |
else if ((wr_flags_ce)&&(~alu_gie)) |
iflags <= r_wr_newflags; |
|
// The 'break' enable bit. This bit can only be set from supervisor |
// mode. It control what the CPU does upon encountering a break |
// instruction. |
// |
// The goal, upon encountering a break is that the CPU should stop and |
// not execute the break instruction, choosing instead to enter into |
// either interrupt mode or halt first. |
// if ((break_en) AND (break_instruction)) // user mode or not |
// HALT CPU |
// else if (break_instruction) // only in user mode |
// set an interrupt flag, set the user break bit, |
// go to supervisor mode, allow supervisor to step the CPU. |
// Upon a CPU halt, any break condition will be reset. The |
// external debugger will then need to deal with whatever |
// condition has taken place. |
initial break_en = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(i_halt)) |
break_en <= 1'b0; |
else if ((wr_reg_ce)&&(~wr_reg_id[4])&&(wr_write_cc)) |
break_en <= wr_spreg_vl[`CPU_BREAK_BIT]; |
|
reg pipe_busy; |
initial pipe_busy <= 1'b0; |
always @(posedge i_clk) |
pipe_busy <= ((mem_ce)||(alu_ce)||(div_ce)||(fpu_ce)) |
||((alu_busy)||(mem_busy)||(div_busy)||(fpu_busy)); |
|
// pending_break <= ((break_en)||(~op_gie))&&(op_break) |
assign o_break = ((op_break)&&(~pipe_busy)&&(~clear_pipeline)) |
||((~r_wr_gie)&&(r_wr_err)); |
|
|
// The GIE register. Only interrupts can disable the interrupt register |
reg slow_interrupt, fast_interrupt; |
initial slow_interrupt = 1'b0; |
// The key difference between a fast interrupt and a slow interrupt |
// is that a fast interrupt requires the pipeline to be cleared, |
// whereas a slow interrupt does not. |
always @(posedge i_clk) |
slow_interrupt <= (gie)&&( |
(i_interrupt) |
// If we encounter a break instruction, if the break |
// enable isn't set. This is slow because pre |
// ALU logic will prevent the break from moving forward. |
||((op_break)&&(~break_en))); |
initial fast_interrupt = 1'b0; |
always @(posedge i_clk) // 12 inputs |
fast_interrupt <= ((gie)||(alu_gie))&&( |
((r_wr_pc_valid)&&(step)&&(~alu_phase)&&(~bus_lock)) |
// Or ... if we encountered some form of error in our |
// instruction ... |
||(r_wr_err) |
// Or if we write to the CC register. |
||((wr_reg_ce)&&(~wr_spreg_vl[`CPU_GIE_BIT]) |
&&(wr_reg_id[4])&&(wr_write_cc))); |
|
assign w_switch_to_interrupt = fast_interrupt; |
|
assign w_release_from_interrupt = (~gie)&&(~i_interrupt) |
// Then if we write the CC register |
&&(((wr_reg_ce)&&(~r_wr_gie)&&(wr_spreg_vl[`CPU_GIE_BIT]) |
&&(~wr_reg_id[4])&&(wr_write_cc)) |
); |
always @(posedge i_clk) |
if (i_rst) |
gie <= 1'b0; |
else if ((fast_interrupt)||(slow_interrupt)) |
gie <= 1'b0; |
else if (w_release_from_interrupt) |
gie <= 1'b1; |
|
initial trap = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
trap <= 1'b0; |
else if (w_release_from_interrupt) |
trap <= 1'b0; |
else if ((r_wr_gie)&&(wr_reg_ce)&&(wr_write_cc) |
&&(~wr_spreg_vl[`CPU_GIE_BIT])) |
// &&(wr_reg_id[4]) implied |
trap <= 1'b1; |
else if ((wr_reg_ce)&&(wr_write_cc)&&(wr_reg_id[4])) |
trap <= wr_spreg_vl[`CPU_TRAP_BIT]; |
|
// The sleep register. Setting the sleep register causes the CPU to |
// sleep until the next interrupt. Setting the sleep register within |
// interrupt mode causes the processor to halt until a reset. This is |
// a panic/fault halt. The trick is that you cannot be allowed to |
// set the sleep bit and switch to supervisor mode in the same |
// instruction: users are not allowed to halt the CPU. |
always @(posedge i_clk) |
if ((i_rst)||(slow_interrupt)) |
sleep <= 1'b0; |
else if ((wr_reg_ce)&&(wr_write_cc)&&(~r_wr_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]; |
|
always @(posedge i_clk) |
if ((i_rst)||(fast_interrupt)) |
step <= 1'b0; |
else if ((wr_reg_ce)&&(~alu_gie)&&(wr_reg_id[4])&&(wr_write_cc)) |
step <= wr_spreg_vl[`CPU_STEP_BIT]; |
else if (((alu_pc_valid)||(mem_pc_valid))&&(step)&&(gie)) |
step <= 1'b0; |
|
|
initial ill_err_i = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
ill_err_i <= 1'b0; |
// Only the debug interface can clear this bit |
else if ((dbgv)&&(wr_reg_id == {1'b0, `CPU_CC_REG}) |
&&(~wr_spreg_vl[`CPU_ILL_BIT])) |
ill_err_i <= 1'b0; |
else if ((r_wr_illegal)&&(~r_wr_gie)) |
ill_err_i <= 1'b1; |
initial 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)) |
ill_err_u <= 1'b0; |
// If the supervisor writes to this register, clearing the |
// bit, then clear it |
else if ((~r_wr_gie) |
&&(wr_reg_ce)&&(~wr_spreg_vl[`CPU_ILL_BIT]) |
&&(wr_reg_id[4])&&(wr_write_cc)) |
ill_err_u <= 1'b0; |
else if ((r_wr_gie)&&(r_wr_illegal)) |
ill_err_u <= 1'b1; |
// 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) |
ibus_err_flag <= 1'b0; |
else if ((dbgv)&&(wr_reg_id == {1'b0, `CPU_CC_REG}) |
&&(~wr_spreg_vl[`CPU_BUSERR_BIT])) |
ibus_err_flag <= 1'b0; |
else if ((bus_err)&&(~alu_gie)) |
ibus_err_flag <= 1'b1; |
// User bus error flag -- if ever set, it will cause an interrupt to |
// supervisor mode. |
initial ubus_err_flag = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
ubus_err_flag <= 1'b0; |
else if (w_release_from_interrupt) |
ubus_err_flag <= 1'b0; |
else if (((~alu_gie)||(dbgv))&&(wr_reg_ce) |
&&(~wr_spreg_vl[`CPU_BUSERR_BIT]) |
&&(wr_reg_id[4])&&(wr_write_cc)) |
ubus_err_flag <= 1'b0; |
else if ((bus_err)&&(alu_gie)) |
ubus_err_flag <= 1'b1; |
|
reg r_idiv_err_flag, r_udiv_err_flag; |
|
// Supervisor/interrupt divide (by zero) error flag -- this will |
// crash the CPU if ever set. This bit is thus available for us |
// to be able to tell if/why the CPU crashed. |
initial r_idiv_err_flag = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
r_idiv_err_flag <= 1'b0; |
else if ((dbgv)&&(wr_reg_id == {1'b0, `CPU_CC_REG}) |
&&(~wr_spreg_vl[`CPU_DIVERR_BIT])) |
r_idiv_err_flag <= 1'b0; |
else if ((div_error)&&(div_valid)&&(~r_wr_gie)) |
r_idiv_err_flag <= 1'b1; |
// 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) |
r_udiv_err_flag <= 1'b0; |
else if (w_release_from_interrupt) |
r_udiv_err_flag <= 1'b0; |
else if (((~r_wr_gie)||(dbgv))&&(wr_reg_ce) |
&&(~wr_spreg_vl[`CPU_DIVERR_BIT]) |
&&(wr_reg_id[4])&&(wr_write_cc)) |
r_udiv_err_flag <= 1'b0; |
else if ((div_error)&&(r_wr_gie)&&(div_valid)) |
r_udiv_err_flag <= 1'b1; |
|
assign idiv_err_flag = r_idiv_err_flag; |
assign udiv_err_flag = r_udiv_err_flag; |
|
generate |
if (IMPLEMENT_FPU !=0) |
begin |
// Supervisor/interrupt floating point error flag -- this will |
// crash the CPU if ever set. |
reg r_ifpu_err_flag, r_ufpu_err_flag; |
initial r_ifpu_err_flag = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
r_ifpu_err_flag <= 1'b0; |
else if ((dbgv)&&(wr_reg_id == {1'b0, `CPU_CC_REG}) |
&&(~wr_spreg_vl[`CPU_FPUERR_BIT])) |
r_ifpu_err_flag <= 1'b0; |
else if ((fpu_error)&&(fpu_valid)&&(~r_wr_gie)) |
r_ifpu_err_flag <= 1'b1; |
// User floating point error flag -- if ever set, it will cause |
// a sudden switch interrupt to supervisor mode. |
initial r_ufpu_err_flag = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
r_ufpu_err_flag <= 1'b0; |
else if (w_release_from_interrupt) |
r_ufpu_err_flag <= 1'b0; |
else if (((~r_wr_gie)||(dbgv))&&(wr_reg_ce) |
&&(~wr_spreg_vl[`CPU_FPUERR_BIT]) |
&&(wr_reg_id[4])&&(wr_write_cc)) |
r_ufpu_err_flag <= 1'b0; |
else if ((fpu_error)&&(r_wr_gie)&&(fpu_valid)) |
r_ufpu_err_flag <= 1'b1; |
|
assign ifpu_err_flag = r_ifpu_err_flag; |
assign ufpu_err_flag = r_ufpu_err_flag; |
end else begin |
assign ifpu_err_flag = 1'b0; |
assign ufpu_err_flag = 1'b0; |
end endgenerate |
|
`ifdef OPT_VLIW |
reg r_ihalt_phase, r_uhalt_phase; |
|
initial r_ihalt_phase = 0; |
initial r_uhalt_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; |
always @(posedge i_clk) |
if (r_wr_gie) |
r_uhalt_phase <= alu_phase; |
else if (w_release_from_interrupt) |
r_uhalt_phase <= 1'b0; |
|
assign ihalt_phase = r_ihalt_phase; |
assign uhalt_phase = r_uhalt_phase; |
`else |
assign ihalt_phase = 1'b0; |
assign uhalt_phase = 1'b0; |
`endif |
|
// |
// Write backs to the PC register, and general increments of it |
// We support two: upc and ipc. If the instruction is normal, |
// we increment upc, if interrupt level we increment ipc. If |
// the instruction writes the PC, we write whichever PC is appropriate. |
// |
// Do we need to all our partial results from the pipeline? |
// 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? |
always @(posedge i_clk) |
if ((wr_reg_ce)&&(wr_reg_id[4])&&(wr_write_pc)) |
upc <= wr_spreg_vl[(AW-1):0]; |
else if ((r_wr_gie)&& |
(((alu_pc_valid)&&(~clear_pipeline)) |
||(mem_pc_valid))) |
upc <= alu_pc; |
|
always @(posedge i_clk) |
if (i_rst) |
ipc <= RESET_ADDRESS; |
else if ((wr_reg_ce)&&(~wr_reg_id[4])&&(wr_write_pc)) |
ipc <= wr_spreg_vl[(AW-1):0]; |
else if ((~r_wr_gie)&& |
(((alu_pc_valid)&&(~clear_pipeline)) |
||(mem_pc_valid))) |
ipc <= alu_pc; |
|
always @(posedge i_clk) |
if (i_rst) |
pf_pc <= RESET_ADDRESS; |
else if ((w_switch_to_interrupt)||((~gie)&&(w_clear_icache))) |
pf_pc <= ipc; |
else if ((w_release_from_interrupt)||((gie)&&(w_clear_icache))) |
pf_pc <= upc; |
else if ((wr_reg_ce)&&(wr_reg_id[4] == gie)&&(wr_write_pc)) |
pf_pc <= wr_spreg_vl[(AW-1):0]; |
`ifdef OPT_PIPELINED |
else if ((dcd_early_branch)&&(~clear_pipeline)) |
pf_pc <= dcd_branch_pc + 1; |
else if ((new_pc)||((~dcd_stalled)&&(pf_valid))) |
pf_pc <= pf_pc + {{(AW-1){1'b0}},1'b1}; |
`else |
else if ((alu_gie==gie)&&( |
((alu_pc_valid)&&(~clear_pipeline)) |
||(mem_pc_valid))) |
pf_pc <= alu_pc; |
`endif |
|
initial new_pc = 1'b1; |
always @(posedge i_clk) |
if ((i_rst)||(i_clear_pf_cache)) |
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)) |
new_pc <= 1'b1; |
else |
new_pc <= 1'b0; |
|
`ifdef OPT_PIPELINED |
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; |
assign w_clear_icache = r_clear_icache; |
`else |
assign w_clear_icache = 1'b0; |
`endif |
|
// |
// The debug interface |
generate |
if (AW<32) |
begin |
always @(posedge i_clk) |
begin |
o_dbg_reg <= regset[i_dbg_reg]; |
if (i_dbg_reg[3:0] == `CPU_PC_REG) |
o_dbg_reg <= {{(32-AW){1'b0}},(i_dbg_reg[4])?upc:ipc}; |
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[31:23] <= w_cpu_info; |
o_dbg_reg[`CPU_GIE_BIT] <= gie; |
end |
end |
end else begin |
always @(posedge i_clk) |
begin |
o_dbg_reg <= regset[i_dbg_reg]; |
if (i_dbg_reg[3:0] == `CPU_PC_REG) |
o_dbg_reg <= (i_dbg_reg[4])?upc:ipc; |
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[31:23] <= w_cpu_info; |
o_dbg_reg[`CPU_GIE_BIT] <= gie; |
end |
end |
end endgenerate |
|
always @(posedge i_clk) |
o_dbg_cc <= { o_break, bus_err, gie, sleep }; |
|
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 |
&&((opvalid)||(i_rst)||(dcd_illegal)) |
// Decode stage must be either valid, in reset, or ill |
&&((dcdvalid)||(i_rst)||(pf_illegal))); |
assign o_dbg_stall = ~r_halted; |
|
// |
// |
// Produce accounting outputs: Account for any CPU stalls, so we can |
// later evaluate how well we are doing. |
// |
// |
assign o_op_stall = (master_ce)&&(op_stall); |
assign o_pf_stall = (master_ce)&&(~pf_valid); |
assign o_i_count = (alu_pc_valid)&&(~clear_pipeline); |
|
`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, |
op_ce, alu_ce, mem_ce, |
// |
master_ce, opvalid_alu, opvalid_mem, |
// |
alu_stall, mem_busy, op_pipe, mem_pipe_stalled, |
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_gpreg_vl[4:0] |
*/ |
/* |
i_rst, master_ce, (new_pc), |
((dcd_early_branch)&&(dcdvalid)), |
pf_valid, pf_illegal, |
op_ce, dcd_ce, dcdvalid, dcd_stalled, |
pf_cyc, pf_stb, pf_we, pf_ack, pf_stall, pf_err, |
pf_pc[7:0], pf_addr[7:0] |
*/ |
|
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] } |
: { instruction[31:21] }, |
pf_valid, (pf_valid) ? alu_pc[14:0] |
:{ pf_cyc, pf_stb, pf_pc[12:0] } |
|
/* |
i_wb_err, gie, new_pc, dcd_early_branch, // 4 |
pf_valid, pf_cyc, pf_stb, instruction_pc[0], // 4 |
instruction[30:27], // 4 |
dcd_gie, mem_busy, o_wb_gbl_cyc, o_wb_gbl_stb, // 4 |
dcdvalid, |
((dcd_early_branch)&&(~clear_pipeline)) // 15 |
? dcd_branch_pc[14:0]:pf_pc[14:0] |
*/ |
}; |
`endif |
|
endmodule |
/rtl/cpu/pipefetch.v
0,0 → 1,299
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: pipefetch.v |
// |
// 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, can be quite a chore. Worse, the Wishbone |
// takes a couple of cycles just to read one instruction from |
// the bus. However, if we use pipeline accesses to the Wishbone |
// bus, then we can read more and faster. Further, if we cache |
// these results so that we have them before we need them, then |
// we have a chance of keeping our CPU from stalling. Those are |
// the purposes of this instruction fetch module: 1) Pipeline |
// wishbone accesses, and 2) an instruction cache. |
// |
// 20150919 -- Fixed a nasty race condition whereby the pipefetch routine |
// would produce either the same instruction twice, or skip |
// an instruction. This condition was dependent on the CPU stall |
// condition, and would only take place if the pipeline wasn't |
// completely full throughout the stall. |
// |
// Interface support was also added for trapping on illegal |
// instructions (i.e., instruction fetches that cause bus errors), |
// however the internal interface has not caught up to supporting |
// these exceptions yet. |
// |
// 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. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
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, |
i_wb_ack, i_wb_stall, i_wb_err, i_wb_data, i_wb_request, |
o_illegal); |
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, |
i_clear_cache, i_stall_n; |
input [(AW-1):0] i_pc; |
output reg [(BUSW-1):0] o_i; |
output reg [(AW-1):0] o_pc; |
output wire o_v; |
// |
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; |
// |
// Is the (data) memory unit also requesting access to the bus? |
input i_wb_request; |
output wire o_illegal; |
|
// Fixed bus outputs: we read from the bus only, never write. |
// Thus the output data is ... irrelevant and don't care. We set it |
// to zero just to set it to something. |
assign o_wb_we = 1'b0; |
assign o_wb_data = 0; |
|
reg [(AW-1):0] r_cache_base; |
reg [(LGCACHELEN):0] r_nvalid, r_acks_waiting; |
reg [(BUSW-1):0] cache[0:(CACHELEN-1)]; |
|
wire [(LGCACHELEN-1):0] w_cache_offset; |
reg [1:0] r_cache_offset; |
|
reg r_addr_set; |
reg [(AW-1):0] r_addr; |
|
wire [(AW-1):0] bus_nvalid; |
assign bus_nvalid = { {(AW-LGCACHELEN-1){1'b0}}, r_nvalid }; |
|
// What are some of the conditions for which we need to restart the |
// cache? |
wire w_pc_out_of_bounds; |
assign w_pc_out_of_bounds = ((i_new_pc)&&((r_nvalid == 0) |
||(i_pc < r_cache_base) |
||(i_pc >= r_cache_base + CACHELEN) |
||(i_pc >= r_cache_base + bus_nvalid+5))); |
wire w_ran_off_end_of_cache; |
assign w_ran_off_end_of_cache =((r_addr_set)&&((r_addr < r_cache_base) |
||(r_addr >= r_cache_base + CACHELEN) |
||(r_addr >= r_cache_base + bus_nvalid+5))); |
wire w_running_out_of_cache; |
assign w_running_out_of_cache = (r_addr_set) |
&&(r_addr >= r_cache_base + |
// {{(AW-LGCACHELEN-1),{1'b0}},2'b11, |
// {(LGCACHELEN-1){1'b0}}}) |
// (1<<(LGCACHELEN-2)) + (1<<(LGCACHELEN-1))) |
+(3<<(LGCACHELEN-2))) |
&&(|r_nvalid[(LGCACHELEN):(LGCACHELEN-1)]); |
|
initial r_cache_base = RESET_ADDRESS; |
always @(posedge i_clk) |
begin |
if ((i_rst)||(i_clear_cache)||((o_wb_cyc)&&(i_wb_err))) |
begin |
o_wb_cyc <= 1'b0; |
o_wb_stb <= 1'b0; |
// r_cache_base <= RESET_ADDRESS; |
// end else if ((~o_wb_cyc)&&(i_new_pc)&&(r_nvalid != 0) |
// &&(i_pc >= r_cache_base) |
// &&(i_pc < r_cache_base + bus_nvalid)) |
// begin |
// The new instruction is in our cache, do nothing |
// with the bus here. |
end else if ((o_wb_cyc)&&(w_pc_out_of_bounds)) |
begin |
// We need to abandon our bus action to start over in |
// a new region, setting up a new cache. This may |
// happen mid cycle while waiting for a result. By |
// dropping o_wb_cyc, we state that we are no longer |
// interested in that result--whatever it might be. |
o_wb_cyc <= 1'b0; |
o_wb_stb <= 1'b0; |
end else if ((~o_wb_cyc)&&(~r_nvalid[LGCACHELEN])&&(~i_wb_request)&&(r_addr_set)) |
begin |
// Restart a bus cycle that was interrupted when the |
// data section wanted access to our bus. |
o_wb_cyc <= 1'b1; |
o_wb_stb <= 1'b1; |
// o_wb_addr <= r_cache_base + bus_nvalid; |
end else if ((~o_wb_cyc)&&( |
(w_pc_out_of_bounds)||(w_ran_off_end_of_cache))) |
begin |
// Start a bus transaction |
o_wb_cyc <= 1'b1; |
o_wb_stb <= 1'b1; |
// o_wb_addr <= (i_new_pc) ? i_pc : r_addr; |
// r_nvalid <= 0; |
// r_cache_base <= (i_new_pc) ? i_pc : r_addr; |
// w_cache_offset <= 0; |
end else if ((~o_wb_cyc)&&(w_running_out_of_cache)) |
begin |
// If we're using the last quarter of the cache, then |
// let's start a bus transaction to extend the cache. |
o_wb_cyc <= 1'b1; |
o_wb_stb <= 1'b1; |
// o_wb_addr <= r_cache_base + (1<<(LGCACHELEN)); |
// r_nvalid <= r_nvalid - (1<<(LGCACHELEN-2)); |
// r_cache_base <= r_cache_base + (1<<(LGCACHELEN-2)); |
// w_cache_offset <= w_cache_offset + (1<<(LGCACHELEN-2)); |
end else if (o_wb_cyc) |
begin |
// This handles everything ... but the case where |
// while reading we need to extend our cache. |
if ((o_wb_stb)&&(~i_wb_stall)) |
begin |
// o_wb_addr <= o_wb_addr + 1; |
if ((o_wb_addr - r_cache_base >= CACHELEN-1) |
||(i_wb_request)) |
o_wb_stb <= 1'b0; |
end |
|
if (i_wb_ack) |
begin |
// r_nvalid <= r_nvalid + 1; |
if ((r_acks_waiting == 1)&&(~o_wb_stb)) |
o_wb_cyc <= 1'b0; |
end else if ((r_acks_waiting == 0)&&(~o_wb_stb)) |
o_wb_cyc <= 1'b0; |
end |
end |
|
|
initial r_nvalid = 0; |
always @(posedge i_clk) |
if ((i_rst)||(i_clear_cache)) // Required, so we can reload memoy and then reset |
r_nvalid <= 0; |
else if ((~o_wb_cyc)&&( |
(w_pc_out_of_bounds)||(w_ran_off_end_of_cache))) |
r_nvalid <= 0; |
else if ((~o_wb_cyc)&&(w_running_out_of_cache)) |
r_nvalid[LGCACHELEN:(LGCACHELEN-2)] |
<= r_nvalid[LGCACHELEN:(LGCACHELEN-2)] +3'b111; |
// i.e. - (1<<(LGCACHELEN-2)); |
else if ((o_wb_cyc)&&(i_wb_ack)) |
r_nvalid <= r_nvalid + {{(LGCACHELEN){1'b0}},1'b1}; // +1; |
|
always @(posedge i_clk) |
if (i_clear_cache) |
r_cache_base <= i_pc; |
else if ((~o_wb_cyc)&&( |
(w_pc_out_of_bounds) |
||(w_ran_off_end_of_cache))) |
r_cache_base <= (i_new_pc) ? i_pc : r_addr; |
else if ((~o_wb_cyc)&&(w_running_out_of_cache)) |
r_cache_base[(AW-1):(LGCACHELEN-2)] |
<= r_cache_base[(AW-1):(LGCACHELEN-2)] |
+ {{(AW-LGCACHELEN+1){1'b0}},1'b1}; |
// i.e. + (1<<(LGCACHELEN-2)); |
|
always @(posedge i_clk) |
if (i_clear_cache) |
r_cache_offset <= 0; |
else if ((~o_wb_cyc)&&( |
(w_pc_out_of_bounds) |
||(w_ran_off_end_of_cache))) |
r_cache_offset <= 0; |
else if ((~o_wb_cyc)&&(w_running_out_of_cache)) |
r_cache_offset[1:0] <= r_cache_offset[1:0] + 2'b01; |
assign w_cache_offset = { r_cache_offset, {(LGCACHELEN-2){1'b0}} }; |
|
always @(posedge i_clk) |
if (i_clear_cache) |
o_wb_addr <= i_pc; |
else if ((o_wb_cyc)&&(w_pc_out_of_bounds)) |
begin |
if (i_wb_ack) |
o_wb_addr <= r_cache_base + bus_nvalid+1; |
else |
o_wb_addr <= r_cache_base + bus_nvalid; |
end else if ((~o_wb_cyc)&&((w_pc_out_of_bounds) |
||(w_ran_off_end_of_cache))) |
o_wb_addr <= (i_new_pc) ? i_pc : r_addr; |
else if ((o_wb_stb)&&(~i_wb_stall)) // && o_wb_cyc |
o_wb_addr <= o_wb_addr + 1; |
|
initial r_acks_waiting = 0; |
always @(posedge i_clk) |
if (~o_wb_cyc) |
r_acks_waiting <= 0; |
// o_wb_cyc *must* be true for all following |
else if ((o_wb_stb)&&(~i_wb_stall)&&(~i_wb_ack)) //&&(o_wb_cyc) |
r_acks_waiting <= r_acks_waiting + {{(LGCACHELEN){1'b0}},1'b1}; |
else if ((i_wb_ack)&&((~o_wb_stb)||(i_wb_stall))) //&&(o_wb_cyc) |
r_acks_waiting <= r_acks_waiting + {(LGCACHELEN+1){1'b1}}; // - 1; |
|
always @(posedge i_clk) |
if ((o_wb_cyc)&&(i_wb_ack)) |
cache[r_nvalid[(LGCACHELEN-1):0]+w_cache_offset] |
<= i_wb_data; |
|
initial r_addr_set = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(i_new_pc)) |
r_addr_set <= 1'b1; |
else if (i_clear_cache) |
r_addr_set <= 1'b0; |
|
// Now, read from the cache |
wire w_cv; // Cache valid, address is in the cache |
reg r_cv; |
assign w_cv = ((r_nvalid != 0)&&(r_addr>=r_cache_base) |
&&(r_addr-r_cache_base < bus_nvalid)); |
always @(posedge i_clk) |
r_cv <= (~i_new_pc)&&((w_cv)||((~i_stall_n)&&(r_cv))); |
assign o_v = (r_cv)&&(~i_new_pc); |
|
always @(posedge i_clk) |
if (i_new_pc) |
r_addr <= i_pc; |
else if ( ((i_stall_n)&&(w_cv)) || ((~i_stall_n)&&(w_cv)&&(r_addr == o_pc)) ) |
r_addr <= r_addr + {{(AW-1){1'b0}},1'b1}; |
|
wire [(LGCACHELEN-1):0] c_rdaddr, c_cache_base; |
assign c_cache_base = r_cache_base[(LGCACHELEN-1):0]; |
assign c_rdaddr = r_addr[(LGCACHELEN-1):0]-c_cache_base+w_cache_offset; |
always @(posedge i_clk) |
if ((~o_v)||((i_stall_n)&&(o_v))) |
o_i <= cache[c_rdaddr]; |
always @(posedge i_clk) |
if ((~o_v)||((i_stall_n)&&(o_v))) |
o_pc <= r_addr; |
|
reg [(AW-1):0] ill_address; |
initial ill_address = 0; |
always @(posedge i_clk) |
if ((o_wb_cyc)&&(i_wb_err)) |
ill_address <= o_wb_addr - {{(AW-LGCACHELEN-1){1'b0}}, r_acks_waiting}; |
|
assign o_illegal = (o_pc == ill_address)&&(~i_rst)&&(~i_new_pc)&&(~i_clear_cache); |
|
|
endmodule |
/rtl/cpu/zipsystem.v
0,0 → 1,823
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: zipsystem.v |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// 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: |
// |
// |
// 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 |
// handling all interrupts. My interrupt controller would work |
// for this purpose. |
// |
// The ZIP-CPU supports only one interrupt because, as I understand |
// 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 |
// only difference is that a watchdog timer's interrupt feeds the |
// reset line instead of the processor interrupt line. |
// |
// Watch-dog timer: this is the same as an interval timer, only it's |
// 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 |
// |
// |
// 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, |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
`include "cpudefs.v" |
// |
// 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! |
// |
// |
// If space is tight, you might not wish to have your performance and |
// accounting counters, so let's make those optional here |
// Without this flag, Slice LUT count is 3315 (ZipSystem),2432 (ZipCPU) |
// When including counters, |
// Slice LUTs ZipSystem ZipCPU |
// With Counters 3315 2432 |
// Without Counters 2796 2046 |
|
// |
// 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] |
|
|
`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 |
`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. |
`define DMAC 5'h10 |
|
// `define RTC_CLOCK 32'hc0000008 // A global something |
// `define BITREV 32'hc0000003 |
// |
// DBGCTRL |
// 10 HALT |
// 9 HALT(ED) |
// 8 STEP (W=1 steps, and returns to halted) |
// 7 INTERRUPT-FLAG |
// 6 RESET_FLAG |
// ADDRESS: |
// 5 PERIPHERAL-BIT |
// [4:0] REGISTER-ADDR |
// DBGDATA |
// read/writes internal registers |
// |
// |
// |
module zipsystem(i_clk, i_rst, |
// Wishbone master interface from the CPU |
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, |
i_wb_ack, i_wb_stall, i_wb_data, i_wb_err, |
// Incoming interrupts |
i_ext_int, |
// Our one outgoing interrupt |
o_ext_int, |
// Wishbone slave interface for debugging purposes |
i_dbg_cyc, i_dbg_stb, i_dbg_we, i_dbg_addr, i_dbg_data, |
o_dbg_ack, o_dbg_stall, o_dbg_data |
`ifdef DEBUG_SCOPE |
, o_cpu_debug |
`endif |
); |
parameter RESET_ADDRESS=24'h0100000, ADDRESS_WIDTH=24, |
LGICACHE=10, START_HALTED=1, EXTERNAL_INTERRUPTS=1, |
`ifdef OPT_MULTIPLY |
IMPLEMENT_MPY = `OPT_MULTIPLY, |
`else |
IMPLEMENT_MPY = 0, |
`endif |
`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, |
HIGHSPEED_CPU=0, |
// Derived parameters |
AW=ADDRESS_WIDTH; |
input i_clk, i_rst; |
// Wishbone master |
output wire o_wb_cyc, o_wb_stb, o_wb_we; |
output wire [(AW-1):0] o_wb_addr; |
output wire [31:0] o_wb_data; |
input i_wb_ack, i_wb_stall; |
input [31:0] i_wb_data; |
input i_wb_err; |
// Incoming interrupts |
input [(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; |
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_cpu_debug; |
`endif |
|
wire [31:0] ext_idata; |
|
// Handle our interrupt vector generation/coordination |
wire [14:0] main_int_vector, alt_int_vector; |
wire ctri_int, tma_int, tmb_int, tmc_int, jif_int, dmac_int; |
wire mtc_int, moc_int, mpc_int, mic_int, |
utc_int, uoc_int, upc_int, uic_int; |
generate |
if (EXTERNAL_INTERRUPTS < 9) |
assign main_int_vector = { {(9-EXTERNAL_INTERRUPTS){1'b0}}, |
i_ext_int, ctri_int, |
tma_int, tmb_int, tmc_int, |
jif_int, dmac_int }; |
else |
assign main_int_vector = { i_ext_int[8:0], ctri_int, |
tma_int, tmb_int, tmc_int, |
jif_int, dmac_int }; |
endgenerate |
generate |
if (EXTERNAL_INTERRUPTS <= 9) |
`ifdef INCLUDE_ACCOUNTING_COUNTERS |
assign alt_int_vector = { 7'h00, |
mtc_int, moc_int, mpc_int, mic_int, |
utc_int, uoc_int, upc_int, uic_int }; |
`else |
assign alt_int_vector = { 15'h00 }; |
`endif |
else |
`ifdef INCLUDE_ACCOUNTING_COUNTERS |
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 |
assign alt_int_vector = { {(15-(EXTERNAL_INTERRUPTS-9)){1'b0}}, |
i_ext_int[(EXTERNAL_INTERRUPTS-1):9] }; |
`endif |
endgenerate |
|
|
// Delay the debug port by one clock, to meet timing requirements |
wire dbg_cyc, dbg_stb, dbg_we, dbg_addr, dbg_stall; |
wire [31:0] dbg_idata, dbg_odata; |
reg dbg_ack; |
`ifdef DELAY_DBG_BUS |
wire dbg_err, no_dbg_err; |
assign dbg_err = 1'b0; |
busdelay #(1,32) wbdelay(i_clk, |
i_dbg_cyc, i_dbg_stb, i_dbg_we, i_dbg_addr, i_dbg_data, |
o_dbg_ack, o_dbg_stall, o_dbg_data, no_dbg_err, |
dbg_cyc, dbg_stb, dbg_we, dbg_addr, dbg_idata, |
dbg_ack, dbg_stall, dbg_odata, dbg_err); |
`else |
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; |
`endif |
|
// |
// |
// |
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 |
// write to set the local address. This interface allows access to |
// the Zip System on a debug basis only, and not to the rest of the |
// wishbone bus. Further, to access these registers, the control |
// register must first be accessed to both stop the CPU and to |
// set the following address in question. Hence all accesses require |
// two accesses: write the address to the control register (and halt |
// the CPU if not halted), then read/write the data from the data |
// register. |
// |
wire cpu_break, dbg_cmd_write; |
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); |
// |
initial cmd_reset = 1'b1; |
always @(posedge i_clk) |
cmd_reset <= ((dbg_cmd_write)&&(dbg_idata[6])); |
// |
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; |
|
initial cmd_clear_pf_cache = 1'b0; |
always @(posedge i_clk) |
cmd_clear_pf_cache = (~i_rst)&&(dbg_cmd_write) |
&&((dbg_idata[11])||(dbg_idata[6])); |
// |
initial cmd_step = 1'b0; |
always @(posedge i_clk) |
cmd_step <= (dbg_cmd_write)&&(dbg_idata[8]); |
// |
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); |
|
wire cpu_halt, cpu_dbg_stall; |
assign cpu_halt = (i_rst)||((cmd_halt)&&(~cmd_step)); |
wire [31:0] pic_data; |
wire [31:0] cmd_data; |
// Values: |
// 0x0003f -> cmd_addr mask |
// 0x00040 -> reset |
// 0x00080 -> PIC interrrupt pending |
// 0x00100 -> cmd_step |
// 0x00200 -> cmd_stall |
// 0x00400 -> cmd_halt |
// 0x00800 -> cmd_clear_pf_cache |
// 0x01000 -> cc.sleep |
// 0x02000 -> cc.gie |
// 0x04000 -> External (PIC) interrupt line is high |
// Other external interrupts follow |
generate |
if (EXTERNAL_INTERRUPTS < 16) |
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, |
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, |
pic_data[15], cpu_reset, cmd_addr }; |
endgenerate |
|
wire cpu_gie; |
assign cpu_gie = cpu_dbg_cc[1]; |
|
// |
// The WATCHDOG Timer |
// |
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, |
sys_data, |
wdt_ack, wdt_stall, wdt_data, wdt_reset); |
|
// |
// Position two, a second watchdog timer--this time for the wishbone |
// bus, in order to tell/find wishbone bus lockups. In its current |
// configuration, it cannot be configured and all bus accesses must |
// take less than the number written to this register. |
// |
reg wdbus_ack; |
reg [(AW-1):0] r_wdbus_data; |
wire [31:0] wdbus_data; |
wire [14:0] wdbus_ignored_data; |
wire reset_wdbus_timer, wdbus_int; |
assign reset_wdbus_timer = ((o_wb_cyc)&&((o_wb_stb)||(i_wb_ack))); |
wbwatchdog #(14) watchbus(i_clk,(cpu_reset)||(reset_wdbus_timer), |
o_wb_cyc, 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 }; |
initial wdbus_ack = 1'b0; |
always @(posedge i_clk) |
wdbus_ack <= ((sys_cyc)&&(sys_stb)&&(sys_addr == 5'h02)); |
|
// Counters -- for performance measurement and accounting |
// |
// Here's the stuff we'll be counting .... |
// |
wire cpu_op_stall, cpu_pf_stall, cpu_i_count; |
|
`ifdef INCLUDE_ACCOUNTING_COUNTERS |
// |
// The master counters will, in general, not be reset. They'll be used |
// for an overall counter. |
// |
// 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), |
sys_we, sys_data, |
mtc_ack, mtc_stall, mtc_data, mtc_int); |
|
// 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), |
sys_we, sys_data, |
moc_ack, moc_stall, moc_data, moc_int); |
|
// 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), |
sys_we, sys_data, |
mpc_ack, mpc_stall, mpc_data, mpc_int); |
|
// 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), |
sys_we, sys_data, |
mic_ack, mic_stall, mic_data, mic_int); |
|
// |
// The user counters are different from those of the master. They will |
// be reset any time a task is given control of the CPU. |
// |
// 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), |
sys_we, sys_data, |
utc_ack, utc_stall, utc_data, utc_int); |
|
// 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), |
sys_we, sys_data, |
uoc_ack, uoc_stall, uoc_data, uoc_int); |
|
// 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), |
sys_we, sys_data, |
upc_ack, upc_stall, upc_data, upc_int); |
|
// 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), |
sys_we, sys_data, |
uic_ack, uic_stall, uic_data, uic_int); |
|
// A little bit of pre-cleanup (actr = accounting counters) |
wire actr_ack, actr_stall; |
wire [31:0] actr_data; |
assign actr_ack = ((mtc_ack | moc_ack | mpc_ack | mic_ack) |
|(utc_ack | uoc_ack | upc_ack | uic_ack)); |
assign actr_stall = ((mtc_stall | moc_stall | mpc_stall | mic_stall) |
|(utc_stall | uoc_stall | upc_stall|uic_stall)); |
assign actr_data = ((mtc_ack) ? mtc_data |
: ((moc_ack) ? moc_data |
: ((mpc_ack) ? mpc_data |
: ((mic_ack) ? mic_data |
: ((utc_ack) ? utc_data |
: ((uoc_ack) ? uoc_data |
: ((upc_ack) ? upc_data |
: uic_data))))))); |
`else // INCLUDE_ACCOUNTING_COUNTERS |
reg actr_ack; |
wire actr_stall; |
wire [31:0] actr_data; |
assign actr_stall = 1'b0; |
assign actr_data = 32'h0000; |
|
assign mtc_int = 1'b0; |
assign moc_int = 1'b0; |
assign mpc_int = 1'b0; |
assign mic_int = 1'b0; |
assign utc_int = 1'b0; |
assign uoc_int = 1'b0; |
assign upc_int = 1'b0; |
assign uic_int = 1'b0; |
|
always @(posedge i_clk) |
actr_ack <= (sys_stb)&&(sys_addr[4:3] == 2'b01); |
`endif // INCLUDE_ACCOUNTING_COUNTERS |
|
// |
// The DMA Controller |
// |
wire dmac_stb, dc_err; |
wire [31:0] dmac_data; |
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 cpu_gbl_cyc; |
assign dmac_stb = (sys_stb)&&(sys_addr[4]); |
`ifdef INCLUDE_DMA_CONTROLLER |
wbdmac #(AW) dma_controller(i_clk, cpu_reset, |
sys_cyc, dmac_stb, sys_we, |
sys_addr[1:0], sys_data, |
dmac_ack, dmac_stall, dmac_data, |
// Need the outgoing DMAC wishbone bus |
dc_cyc, dc_stb, dc_we, dc_addr, dc_data, |
dc_ack, dc_stall, ext_idata, dc_err, |
// External device interrupts |
{ 1'b0, alt_int_vector, 1'b0, |
main_int_vector[14:1], 1'b0 }, |
// DMAC interrupt, for upon completion |
dmac_int); |
`else |
reg r_dmac_ack; |
always @(posedge i_clk) |
r_dmac_ack <= (sys_cyc)&&(dmac_stb); |
assign dmac_ack = r_dmac_ack; |
assign dmac_data = 32'h000; |
assign dmac_stall = 1'b0; |
|
assign dc_cyc = 1'b0; |
assign dc_stb = 1'b0; |
assign dc_we = 1'b0; |
assign dc_addr = { (AW) {1'b0} }; |
assign dc_data = 32'h00; |
|
assign dmac_int = 1'b0; |
`endif |
|
wire ctri_sel, ctri_stall; |
reg ctri_ack; |
wire [31:0] ctri_data; |
assign ctri_sel = (sys_cyc)&&(sys_stb)&&(sys_addr == `CTRINT); |
always @(posedge i_clk) |
ctri_ack <= ctri_sel; |
assign ctri_stall = 1'b0; |
`ifdef INCLUDE_ACCOUNTING_COUNTERS |
// |
// Counter Interrupt controller |
// |
generate |
if (EXTERNAL_INTERRUPTS <= 9) |
begin |
icontrol #(8) ctri(i_clk, cpu_reset, (ctri_sel), |
sys_data, ctri_data, alt_int_vector[7:0], |
ctri_int); |
end else begin |
icontrol #(8+(EXTERNAL_INTERRUPTS-9)) |
ctri(i_clk, cpu_reset, (ctri_sel), |
sys_data, ctri_data, |
alt_int_vector[(EXTERNAL_INTERRUPTS-2):0], |
ctri_int); |
end endgenerate |
|
`else // INCLUDE_ACCOUNTING_COUNTERS |
|
generate |
if (EXTERNAL_INTERRUPTS <= 9) |
begin |
assign ctri_stall = 1'b0; |
assign ctri_data = 32'h0000; |
assign ctri_int = 1'b0; |
end else begin |
icontrol #(EXTERNAL_INTERRUPTS-9) |
ctri(i_clk, cpu_reset, (ctri_sel), |
sys_data, ctri_data, |
alt_int_vector[(EXTERNAL_INTERRUPTS-10):0], |
ctri_int); |
end endgenerate |
`endif // INCLUDE_ACCOUNTING_COUNTERS |
|
|
// |
// Timer A |
// |
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); |
|
// |
// Timer B |
// |
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); |
|
// |
// Timer C |
// |
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); |
|
// |
// JIFFIES |
// |
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, |
sys_data, |
jif_ack, jif_stall, jif_data, jif_int); |
|
// |
// The programmable interrupt controller peripheral |
// |
wire pic_interrupt; |
generate |
if (EXTERNAL_INTERRUPTS < 9) |
begin |
icontrol #(6+EXTERNAL_INTERRUPTS) pic(i_clk, cpu_reset, |
(sys_cyc)&&(sys_stb)&&(sys_we) |
&&(sys_addr==`INTCTRL), |
sys_data, pic_data, |
main_int_vector[(6+EXTERNAL_INTERRUPTS-1):0], pic_interrupt); |
end else begin |
icontrol #(15) pic(i_clk, cpu_reset, |
(sys_cyc)&&(sys_stb)&&(sys_we) |
&&(sys_addr==`INTCTRL), |
sys_data, pic_data, |
main_int_vector[14:0], pic_interrupt); |
end endgenerate |
|
wire pic_stall; |
assign pic_stall = 1'b0; |
reg pic_ack; |
always @(posedge i_clk) |
pic_ack <= (sys_cyc)&&(sys_stb)&&(sys_addr == `INTCTRL); |
|
// |
// The CPU itself |
// |
wire cpu_gbl_stb, cpu_lcl_cyc, cpu_lcl_stb, |
cpu_we, cpu_dbg_we; |
wire [31:0] cpu_data, wb_data; |
wire cpu_ack, cpu_stall, cpu_err; |
wire [31:0] cpu_dbg_data; |
assign cpu_dbg_we = ((dbg_cyc)&&(dbg_stb)&&(~cmd_addr[5]) |
&&(dbg_we)&&(dbg_addr)); |
|
generate |
if (HIGHSPEED_CPU==0) |
begin |
zipcpu #( |
.RESET_ADDRESS(RESET_ADDRESS), |
.ADDRESS_WIDTH(ADDRESS_WIDTH), |
.LGICACHE(LGICACHE), |
.IMPLEMENT_MPY(IMPLEMENT_MPY), |
.IMPLEMENT_DIVIDE(IMPLEMENT_DIVIDE), |
.IMPLEMENT_FPU(IMPLEMENT_FPU), |
.IMPLEMENT_LOCK(IMPLEMENT_LOCK) |
) |
thecpu(i_clk, cpu_reset, pic_interrupt, |
cpu_halt, cmd_clear_pf_cache, cmd_addr[4:0], cpu_dbg_we, |
dbg_idata, cpu_dbg_stall, cpu_dbg_data, |
cpu_dbg_cc, cpu_break, |
cpu_gbl_cyc, cpu_gbl_stb, |
cpu_lcl_cyc, cpu_lcl_stb, |
cpu_we, cpu_addr, cpu_data, |
cpu_ack, cpu_stall, wb_data, |
cpu_err, |
cpu_op_stall, cpu_pf_stall, cpu_i_count |
`ifdef DEBUG_SCOPE |
, o_cpu_debug |
`endif |
); |
end else begin |
zipcpu #( |
.RESET_ADDRESS(RESET_ADDRESS), |
.ADDRESS_WIDTH(ADDRESS_WIDTH), |
.LGICACHE(LGICACHE), |
.IMPLEMENT_MPY(IMPLEMENT_MPY), |
.IMPLEMENT_DIVIDE(IMPLEMENT_DIVIDE), |
.IMPLEMENT_FPU(IMPLEMENT_FPU), |
.IMPLEMENT_LOCK(IMPLEMENT_LOCK) |
) |
thecpu(i_clk, cpu_reset, pic_interrupt, |
cpu_halt, cmd_clear_pf_cache, cmd_addr[4:0], cpu_dbg_we, |
dbg_idata, cpu_dbg_stall, cpu_dbg_data, |
cpu_dbg_cc, cpu_break, |
cpu_gbl_cyc, cpu_gbl_stb, |
cpu_lcl_cyc, cpu_lcl_stb, |
cpu_we, cpu_addr, cpu_data, |
cpu_ack, cpu_stall, wb_data, |
cpu_err, |
cpu_op_stall, cpu_pf_stall, cpu_i_count |
`ifdef DEBUG_SCOPE |
, o_cpu_debug |
`endif |
); |
end endgenerate |
|
// 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) |
// (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, |
// or |
// (~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])); |
assign sys_cyc = (cpu_lcl_cyc)||(sys_dbg_cyc); |
assign sys_stb = (cpu_lcl_cyc) |
? (cpu_lcl_stb) |
: ((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_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); |
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); |
|
// Now for the external wishbone bus |
// Need to arbitrate between the flash cache and the CPU |
// The way this works, though, the CPU will stall once the flash |
// 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 [31:0] ext_odata; |
wbpriarbiter #(32,AW) dmacvcpu(i_clk, |
cpu_gbl_cyc, cpu_gbl_stb, cpu_we, cpu_addr, cpu_data, |
cpu_ext_ack, cpu_ext_stall, cpu_ext_err, |
dc_cyc, dc_stb, dc_we, dc_addr, dc_data, |
dc_ack, dc_stall, dc_err, |
ext_cyc, ext_stb, ext_we, ext_addr, ext_odata, |
ext_ack, ext_stall, ext_err); |
|
`ifdef DELAY_EXT_BUS |
busdelay #(AW,32) extbus(i_clk, |
ext_cyc, ext_stb, ext_we, ext_addr, ext_odata, |
ext_ack, ext_stall, ext_idata, ext_err, |
o_wb_cyc, o_wb_stb, o_wb_we, o_wb_addr, o_wb_data, |
i_wb_ack, i_wb_stall, i_wb_data, (i_wb_err)||(wdbus_int)); |
`else |
assign o_wb_cyc = ext_cyc; |
assign o_wb_stb = ext_stb; |
assign o_wb_we = ext_we; |
assign o_wb_addr = ext_addr; |
assign o_wb_data = ext_odata; |
assign ext_ack = i_wb_ack; |
assign ext_stall = i_wb_stall; |
assign ext_idata = i_wb_data; |
assign ext_err = (i_wb_err)||(wdbus_int); |
`endif |
|
wire tmr_ack; |
assign tmr_ack = (tma_ack|tmb_ack|tmc_ack|jif_ack); |
wire [31:0] tmr_data; |
assign tmr_data = (tma_ack)?tma_data |
:(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)))); |
|
assign sys_stall = (tma_stall | tmb_stall | tmc_stall | jif_stall |
| wdt_stall | ctri_stall | actr_stall |
| pic_stall | dmac_stall); |
assign cpu_stall = (sys_stall)|(cpu_ext_stall); |
assign sys_ack = (tmr_ack|wdt_ack|ctri_ack|actr_ack|pic_ack|dmac_ack|wdbus_ack); |
assign cpu_ack = (sys_ack)||(cpu_ext_ack); |
assign cpu_err = (cpu_ext_err)&&(cpu_gbl_cyc); |
|
assign o_ext_int = (cmd_halt) && (~cpu_stall); |
|
endmodule |
/rtl/cpu/busdelay.v
0,0 → 1,96
/////////////////////////////////////////////////////////////////////////// |
// |
// 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. This |
// particular version of the busdelay builds off of some previous |
// work, but also delays and buffers the stall line as well. It is |
// designed to allow pipelined accesses (1 access/clock) to still work, |
// while also providing for single accesses. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
module busdelay(i_clk, |
// The input bus |
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_wb_err, |
// The delayed bus |
o_dly_cyc, o_dly_stb, o_dly_we, o_dly_addr, o_dly_data, |
i_dly_ack, i_dly_stall, i_dly_data, i_dly_err); |
parameter AW=32, DW=32; |
input i_clk; |
// Input/master bus |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [(AW-1):0] i_wb_addr; |
input [(DW-1):0] i_wb_data; |
output reg o_wb_ack; |
output reg 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_we; |
output wire o_dly_stb; |
output reg [(AW-1):0] o_dly_addr; |
output reg [(DW-1):0] o_dly_data; |
input i_dly_ack; |
input i_dly_stall; |
input [(DW-1):0] i_dly_data; |
input i_dly_err; |
|
reg loaded; |
initial o_dly_cyc = 1'b0; |
initial loaded = 1'b0; |
|
always @(posedge i_clk) |
o_wb_stall <= (loaded)&&(i_dly_stall); |
|
initial o_dly_cyc = 1'b0; |
always @(posedge i_clk) |
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) |
loaded <= (i_wb_stb)||((loaded)&&(i_dly_stall)&&(~i_dly_err)&&(i_wb_cyc)); |
assign o_dly_stb = loaded; |
always @(posedge i_clk) |
if (~i_dly_stall) |
o_dly_we <= i_wb_we; |
always @(posedge i_clk) |
if (~i_dly_stall) |
o_dly_addr<= i_wb_addr; |
always @(posedge i_clk) |
if (~i_dly_stall) |
o_dly_data <= i_wb_data; |
always @(posedge i_clk) |
o_wb_ack <= (i_dly_ack)&&(o_dly_cyc)&&(i_wb_cyc); |
always @(posedge i_clk) |
o_wb_data <= i_dly_data; |
|
always @(posedge i_clk) |
o_wb_err <= (i_dly_err)&&(o_dly_cyc)&&(i_wb_cyc); |
|
endmodule |
/rtl/cpu/cpuops.v
0,0 → 1,368
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: cpuops.v |
// |
// 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). |
// |
// |
// 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. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
`define LONG_MPY |
module cpuops(i_clk,i_rst, i_ce, i_valid, i_op, i_a, i_b, o_c, o_f, o_valid, |
o_illegal, o_busy); |
parameter IMPLEMENT_MPY = 1; |
input i_clk, i_rst, i_ce; |
input [3:0] i_op; |
input [31:0] i_a, i_b; |
input i_valid; |
output reg [31:0] o_c; |
output wire [3:0] o_f; |
output reg o_valid; |
output wire o_illegal; |
output wire o_busy; |
|
// Rotate-left pre-logic |
wire [63:0] w_rol_tmp; |
assign w_rol_tmp = { i_a, i_a } << i_b[4:0]; |
wire [31:0] w_rol_result; |
assign w_rol_result = w_rol_tmp[63:32]; // Won't set flags |
|
// 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 |
|
// 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 |
assign w_brev_result[k] = i_b[31-k]; |
end endgenerate |
|
// Popcount pre-logic |
wire [31:0] w_popc_result; |
assign w_popc_result[5:0]= |
({5'h0,i_b[ 0]}+{5'h0,i_b[ 1]}+{5'h0,i_b[ 2]}+{5'h0,i_b[ 3]}) |
+({5'h0,i_b[ 4]}+{5'h0,i_b[ 5]}+{5'h0,i_b[ 6]}+{5'h0,i_b[ 7]}) |
+({5'h0,i_b[ 8]}+{5'h0,i_b[ 9]}+{5'h0,i_b[10]}+{5'h0,i_b[11]}) |
+({5'h0,i_b[12]}+{5'h0,i_b[13]}+{5'h0,i_b[14]}+{5'h0,i_b[15]}) |
+({5'h0,i_b[16]}+{5'h0,i_b[17]}+{5'h0,i_b[18]}+{5'h0,i_b[19]}) |
+({5'h0,i_b[20]}+{5'h0,i_b[21]}+{5'h0,i_b[22]}+{5'h0,i_b[23]}) |
+({5'h0,i_b[24]}+{5'h0,i_b[25]}+{5'h0,i_b[26]}+{5'h0,i_b[27]}) |
+({5'h0,i_b[28]}+{5'h0,i_b[29]}+{5'h0,i_b[30]}+{5'h0,i_b[31]}); |
assign w_popc_result[31:6] = 26'h00; |
|
// Prelogic for our flags registers |
wire z, n, v; |
reg c, pre_sign, set_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 |
|
`ifdef LONG_MPY |
reg mpyhi; |
wire mpybusy; |
`endif |
|
// 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. |
generate |
if (IMPLEMENT_MPY == 0) |
begin |
always @(posedge i_clk) |
if (i_ce) |
begin |
pre_sign <= (i_a[31]); |
c <= 1'b0; |
casez(i_op) |
4'b0000:{c,o_c } <= {1'b0,i_a}-{1'b0,i_b};// CMP/SUB |
4'b0001: o_c <= i_a & i_b; // BTST/And |
4'b0010:{c,o_c } <= i_a + i_b; // Add |
4'b0011: o_c <= i_a | i_b; // Or |
4'b0100: o_c <= i_a ^ i_b; // Xor |
4'b0101:{o_c,c } <= w_lsr_result[32:0]; // LSR |
4'b0110:{c,o_c } <= w_lsl_result[32:0]; // LSL |
4'b0111:{o_c,c } <= w_asr_result[32:0]; // ASR |
`ifndef LONG_MPY |
4'b1000: o_c <= { i_b[15: 0], i_a[15:0] }; // LODIHI |
`endif |
4'b1001: o_c <= { i_a[31:16], i_b[15:0] }; // LODILO |
// 4'h1010: The unimplemented MPYU, |
// 4'h1011: and here for the unimplemented MPYS |
4'b1100: o_c <= w_brev_result; // BREV |
4'b1101: o_c <= w_popc_result; // POPC |
4'b1110: o_c <= w_rol_result; // ROL |
default: o_c <= i_b; // MOV, LDI |
endcase |
end |
|
assign o_busy = 1'b0; |
|
reg r_illegal; |
always @(posedge i_clk) |
r_illegal <= (i_ce)&&((i_op == 4'ha)||(i_op == 4'hb) |
`ifdef LONG_MPY |
||(i_op == 4'h8) |
`endif |
); |
assign o_illegal = r_illegal; |
end else begin |
// |
// Multiply pre-logic |
// |
`ifdef LONG_MPY |
reg [63:0] r_mpy_result; |
if (IMPLEMENT_MPY == 1) |
begin // Our two clock option (one clock extra) |
reg signed [64:0] r_mpy_a_input, r_mpy_b_input; |
reg mpypipe, x; |
initial mpypipe = 1'b0; |
always @(posedge i_clk) |
mpypipe <= (i_ce)&&((i_op[3:1]==3'h5)||(i_op[3:0]==4'h8)); |
always @(posedge i_clk) |
if (i_ce) |
begin |
r_mpy_a_input <= {{(33){(i_a[31])&(i_op[0])}}, |
i_a[31:0]}; |
r_mpy_b_input <= {{(33){(i_b[31])&(i_op[0])}}, |
i_b[31:0]}; |
end |
always @(posedge i_clk) |
if (mpypipe) |
{x, r_mpy_result} = r_mpy_a_input |
* r_mpy_b_input; |
always @(posedge i_clk) |
if (i_ce) |
mpyhi = i_op[1]; |
assign mpybusy = mpypipe; |
end else if (IMPLEMENT_MPY == 2) |
begin // The three clock option |
reg [31:0] r_mpy_a_input, r_mpy_b_input; |
reg r_mpy_signed; |
reg [1:0] mpypipe; |
|
// First clock, latch in the inputs |
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. |
mpypipe[0] <= (i_ce)&&((i_op[3:1]==3'h5) |
||(i_op[3:0]==4'h8)); |
mpypipe[1] <= mpypipe[0]; |
|
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_ce) |
mpyhi = i_op[1]; |
end |
|
assign mpybusy = |mpypipe; |
|
// 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 |
end // Fourth clock -- results are available for writeback. |
`else |
wire signed [16:0] w_mpy_a_input, w_mpy_b_input; |
wire [33:0] w_mpy_result; |
reg [31:0] r_mpy_result; |
assign w_mpy_a_input ={ ((i_a[15])&(i_op[0])), i_a[15:0] }; |
assign w_mpy_b_input ={ ((i_b[15])&(i_op[0])), i_b[15:0] }; |
assign w_mpy_result = w_mpy_a_input * w_mpy_b_input; |
always @(posedge i_clk) |
if (i_ce) |
r_mpy_result = w_mpy_result[31:0]; |
`endif |
|
// |
// The master ALU case statement |
// |
always @(posedge i_clk) |
if (i_ce) |
begin |
pre_sign <= (i_a[31]); |
c <= 1'b0; |
casez(i_op) |
4'b0000:{c,o_c } <= {1'b0,i_a}-{1'b0,i_b};// CMP/SUB |
4'b0001: o_c <= i_a & i_b; // BTST/And |
4'b0010:{c,o_c } <= i_a + i_b; // Add |
4'b0011: o_c <= i_a | i_b; // Or |
4'b0100: o_c <= i_a ^ i_b; // Xor |
4'b0101:{o_c,c } <= w_lsr_result[32:0]; // LSR |
4'b0110:{c,o_c } <= w_lsl_result[32:0]; // LSL |
4'b0111:{o_c,c } <= w_asr_result[32:0]; // ASR |
`ifdef LONG_MPY |
4'b1000: o_c <= r_mpy_result[31:0]; // MPY |
`else |
4'b1000: o_c <= { i_b[15: 0], i_a[15:0] }; // LODIHI |
`endif |
4'b1001: o_c <= { i_a[31:16], i_b[15:0] }; // LODILO |
`ifdef LONG_MPY |
4'b1010: o_c <= r_mpy_result[63:32]; // MPYHU |
4'b1011: o_c <= r_mpy_result[63:32]; // MPYHS |
`else |
4'b1010: o_c <= r_mpy_result; // MPYU |
4'b1011: o_c <= r_mpy_result; // MPYS |
`endif |
4'b1100: o_c <= w_brev_result; // BREV |
4'b1101: o_c <= w_popc_result; // POPC |
4'b1110: o_c <= w_rol_result; // ROL |
default: o_c <= i_b; // MOV, LDI |
endcase |
end else if (r_busy) |
`ifdef LONG_MPY |
o_c <= (mpyhi)?r_mpy_result[63:32]:r_mpy_result[31:0]; |
`else |
o_c <= r_mpy_result; |
`endif |
|
reg r_busy; |
initial r_busy = 1'b0; |
always @(posedge i_clk) |
r_busy <= (~i_rst)&&(i_ce)&&(i_valid) |
`ifdef LONG_MPY |
&&((i_op[3:1] == 3'h5) |
||(i_op[3:0] == 4'h8))||mpybusy; |
`else |
&&(i_op[3:1] == 3'h5); |
`endif |
|
assign o_busy = r_busy; |
|
assign o_illegal = 1'b0; |
end endgenerate |
|
assign z = (o_c == 32'h0000); |
assign n = (o_c[31]); |
assign v = (set_ovfl)&&(pre_sign != o_c[31]); |
|
assign o_f = { v, n, c, z }; |
|
initial o_valid = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
o_valid <= 1'b0; |
else |
o_valid <= (i_ce)&&(i_valid) |
`ifdef LONG_MPY |
&&(i_op[3:1] != 3'h5)&&(i_op[3:0] != 4'h8) |
||(o_busy)&&(~mpybusy); |
`else |
&&(i_op[3:1] != 3'h5)||(o_busy); |
`endif |
endmodule |
/rtl/cpu/zipcpu.v
0,0 → 1,1866
/////////////////////////////////////////////////////////////////////////////// |
// |
// 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 |
// RISC as you can get, there are only 16 instruction types supported. |
// Please see the accompanying spec.pdf file for a description of these |
// instructions. |
// |
// All instructions are 32-bits wide. All bus accesses, both address and |
// data, are 32-bits over a wishbone bus. |
// |
// The Zip CPU is fully pipelined with the following pipeline stages: |
// |
// 1. Prefetch, returns the instruction from memory. |
// |
// 2. Instruction Decode |
// |
// 3. Read Operands |
// |
// 4. Apply Instruction |
// |
// 4. Write-back Results |
// |
// Further information about the inner workings of this CPU may be |
// found in the spec.pdf file. (The documentation within this file |
// had become out of date and out of sync with the spec.pdf, so look |
// to the spec.pdf for accurate and up to date information.) |
// |
// |
// In general, the pipelining is controlled by three pieces of logic |
// per stage: _ce, _stall, and _valid. _valid means that the stage |
// holds a valid instruction. _ce means that the instruction from the |
// previous stage is to move into this one, and _stall means that the |
// instruction from the previous stage may not move into this one. |
// The difference between these control signals allows individual stages |
// to propagate instructions independently. In general, the logic works |
// as: |
// |
// |
// assign (n)_ce = (n-1)_valid && (~(n)_stall) |
// |
// |
// always @(posedge i_clk) |
// if ((i_rst)||(clear_pipeline)) |
// (n)_valid = 0 |
// else if (n)_ce |
// (n)_valid = 1 |
// else if (n+1)_ce |
// (n)_valid = 0 |
// |
// assign (n)_stall = ( (n-1)_valid && ( pipeline hazard detection ) ) |
// || ( (n)_valid && (n+1)_stall ); |
// |
// and ... |
// |
// always @(posedge i_clk) |
// if (n)_ce |
// (n)_variable = ... whatever logic for this stage |
// |
// 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-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////////// |
// |
// We can either pipeline our fetches, or issue one fetch at a time. Pipelined |
// fetches are more complicated and therefore use more FPGA resources, while |
// single fetches will cause the CPU to stall for about 5 stalls each |
// instruction cycle, effectively reducing the instruction count per clock to |
// about 0.2. However, the area cost may be worth it. Consider: |
// |
// Slice LUTs ZipSystem ZipCPU |
// Single Fetching 2521 1734 |
// Pipelined fetching 2796 2046 |
// |
// |
// |
`define CPU_CC_REG 4'he |
`define CPU_PC_REG 4'hf |
`define CPU_CLRCACHE_BIT 14 // Floating point error flag, set on error |
`define CPU_PHASE_BIT 13 // Floating point error flag, set on error |
`define CPU_FPUERR_BIT 12 // Floating point error flag, set on error |
`define CPU_DIVERR_BIT 11 // Divide error flag, set on divide by zero |
`define CPU_BUSERR_BIT 10 // Bus error flag, set on error |
`define CPU_TRAP_BIT 9 // User TRAP has taken place |
`define CPU_ILL_BIT 8 // Illegal instruction |
`define CPU_BREAK_BIT 7 |
`define CPU_STEP_BIT 6 // Will step one or two (VLIW) instructions |
`define CPU_GIE_BIT 5 |
`define CPU_SLEEP_BIT 4 |
// Compile time defines |
// |
`include "cpudefs.v" |
// |
// |
module zipcpu(i_clk, i_rst, i_interrupt, |
// Debug interface |
i_halt, i_clear_pf_cache, i_dbg_reg, i_dbg_we, i_dbg_data, |
o_dbg_stall, o_dbg_reg, o_dbg_cc, |
o_break, |
// CPU interface to the wishbone bus |
o_wb_gbl_cyc, o_wb_gbl_stb, |
o_wb_lcl_cyc, o_wb_lcl_stb, |
o_wb_we, o_wb_addr, o_wb_data, |
i_wb_ack, i_wb_stall, i_wb_data, |
i_wb_err, |
// Accounting/CPU usage interface |
o_op_stall, o_pf_stall, o_i_count |
`ifdef DEBUG_SCOPE |
, o_debug |
`endif |
); |
parameter RESET_ADDRESS=32'h0100000, ADDRESS_WIDTH=24, |
LGICACHE=6; |
`ifdef OPT_MULTIPLY |
parameter IMPLEMENT_MPY = `OPT_MULTIPLY; |
`else |
parameter IMPLEMENT_MPY = 0; |
`endif |
`ifdef OPT_DIVIDE |
parameter IMPLEMENT_DIVIDE = 1; |
`else |
parameter IMPLEMENT_DIVIDE = 0; |
`endif |
`ifdef OPT_IMPLEMENT_FPU |
parameter IMPLEMENT_FPU = 1, |
`else |
parameter IMPLEMENT_FPU = 0, |
`endif |
IMPLEMENT_LOCK=1; |
`ifdef OPT_EARLY_BRANCHING |
parameter EARLY_BRANCHING = 1; |
`else |
parameter EARLY_BRANCHING = 0; |
`endif |
parameter AW=ADDRESS_WIDTH; |
input i_clk, i_rst, 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; |
// Debug interface -- outputs |
output wire o_dbg_stall; |
output reg [31:0] o_dbg_reg; |
output reg [3:0] o_dbg_cc; |
output wire o_break; |
// Wishbone interface -- outputs |
output wire o_wb_gbl_cyc, o_wb_gbl_stb; |
output wire o_wb_lcl_cyc, o_wb_lcl_stb, o_wb_we; |
output wire [(AW-1):0] o_wb_addr; |
output wire [31:0] o_wb_data; |
// Wishbone interface -- inputs |
input i_wb_ack, i_wb_stall; |
input [31:0] i_wb_data; |
input i_wb_err; |
// Accounting outputs ... to help us count stalls and usage |
output wire o_op_stall; |
output wire o_pf_stall; |
output wire o_i_count; |
// |
`ifdef DEBUG_SCOPE |
output reg [31:0] o_debug; |
`endif |
|
|
// Registers |
// |
// The distributed RAM style comment is necessary on the |
// SPARTAN6 with XST to prevent XST from oversimplifying the register |
// set and in the process ruining everything else. It basically |
// optimizes logic away, to where it no longer works. The logic |
// as described herein will work, this just makes sure XST implements |
// that logic. |
// |
(* ram_style = "distributed" *) |
reg [31:0] regset [0:31]; |
|
// Condition codes |
// (BUS, TRAP,ILL,BREAKEN,STEP,GIE,SLEEP ), V, N, C, Z |
reg [3:0] flags, iflags; |
wire [14:0] w_uflags, w_iflags; |
reg trap, break_en, step, gie, sleep, r_halted, |
break_pending; |
wire w_clear_icache; |
`ifdef OPT_ILLEGAL_INSTRUCTION |
reg ill_err_u, ill_err_i; |
`else |
wire ill_err_u, ill_err_i; |
`endif |
reg ubreak; |
reg ibus_err_flag, ubus_err_flag; |
wire idiv_err_flag, udiv_err_flag; |
wire ifpu_err_flag, ufpu_err_flag; |
wire ihalt_phase, uhalt_phase; |
|
// The master chip enable |
wire master_ce; |
|
// |
// |
// PIPELINE STAGE #1 :: Prefetch |
// Variable declarations |
// |
reg [(AW-1):0] pf_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; |
wire [(AW-1):0] pf_addr; |
wire [31:0] pf_data; |
wire [31:0] instruction; |
wire [(AW-1):0] instruction_pc; |
wire pf_valid, instruction_gie, pf_illegal; |
|
// |
// |
// PIPELINE STAGE #2 :: Instruction Decode |
// Variable declarations |
// |
// |
reg opvalid, opvalid_mem, opvalid_alu; |
reg opvalid_div, opvalid_fpu; |
wire op_stall, dcd_ce, dcd_phase; |
wire [3:0] dcdOp; |
wire [4:0] dcdA, dcdB, dcdR; |
wire dcdA_cc, dcdB_cc, dcdA_pc, dcdB_pc, dcdR_cc, dcdR_pc; |
wire [3:0] dcdF; |
wire dcdR_wr, dcdA_rd, dcdB_rd, |
dcdALU, dcdM, dcdDV, dcdFP, |
dcdF_wr, dcd_gie, dcd_break, dcd_lock, |
dcd_pipe, dcd_ljmp; |
reg r_dcdvalid; |
wire dcdvalid; |
wire [(AW-1):0] dcd_pc; |
wire [31:0] dcdI; |
wire dcd_zI; // true if dcdI == 0 |
wire dcdA_stall, dcdB_stall, dcdF_stall; |
|
wire dcd_illegal; |
wire dcd_early_branch; |
wire [(AW-1):0] dcd_branch_pc; |
|
|
// |
// |
// PIPELINE STAGE #3 :: Read Operands |
// Variable declarations |
// |
// |
// |
// Now, let's read our operands |
reg [4:0] alu_reg; |
reg [3:0] opn; |
reg [4:0] opR; |
reg [31:0] r_opA, r_opB; |
reg [(AW-1):0] op_pc; |
wire [31:0] w_opA, w_opB; |
wire [31:0] opA_nowait, opB_nowait, opA, opB; |
reg opR_wr, opR_cc, opF_wr, op_gie; |
wire [14:0] opFl; |
reg [5:0] r_opF; |
wire [7:0] opF; |
wire op_ce, op_phase, op_pipe, op_change_data_ce; |
// Some pipeline control wires |
`ifdef OPT_PIPELINED |
reg opA_alu, opA_mem; |
reg opB_alu, opB_mem; |
`endif |
`ifdef OPT_ILLEGAL_INSTRUCTION |
reg op_illegal; |
`else |
wire op_illegal; |
assign op_illegal = 1'b0; |
`endif |
reg op_break; |
wire op_lock; |
|
|
// |
// |
// PIPELINE STAGE #4 :: ALU / Memory |
// Variable declarations |
// |
// |
reg [(AW-1):0] alu_pc; |
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; |
wire [3:0] alu_flags; |
wire alu_valid, alu_busy; |
wire set_cond; |
reg alu_wr, alF_wr, alu_gie; |
wire alu_illegal_op; |
wire alu_illegal; |
|
|
|
wire mem_ce, mem_stalled; |
`ifdef OPT_PIPELINED_BUS_ACCESS |
wire mem_pipe_stalled; |
`endif |
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; |
wire [(AW-1):0] mem_addr; |
wire [31:0] mem_data, mem_result; |
|
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)&&(opvalid_div) |
&&(~mem_rdbusy)&&(~div_busy)&&(~fpu_busy) |
&&(set_cond); |
|
wire fpu_ce, fpu_error, fpu_busy, fpu_valid; |
wire [31:0] fpu_result; |
wire [3:0] fpu_flags; |
|
assign fpu_ce = (master_ce)&&(~clear_pipeline)&&(opvalid_fpu) |
&&(~mem_rdbusy)&&(~div_busy)&&(~fpu_busy) |
&&(set_cond); |
|
wire adf_ce_unconditional; |
|
// |
// |
// 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; |
reg [(AW-1):0] upc, ipc; |
|
|
|
// |
// MASTER: clock enable. |
// |
assign master_ce = (~i_halt)&&(~o_break)&&(~sleep); |
|
|
// |
// PIPELINE STAGE #1 :: Prefetch |
// Calculate stall conditions |
// |
// These are calculated externally, within the prefetch module. |
// |
|
// |
// PIPELINE STAGE #2 :: Instruction Decode |
// Calculate stall conditions |
assign dcd_ce = ((~dcdvalid)||(~dcd_stalled))&&(~clear_pipeline); |
|
`ifdef OPT_PIPELINED |
assign dcd_stalled = (dcdvalid)&&(op_stall); |
`else |
// If not pipelined, there will be no opvalid_ anything, and the |
// op_stall will be false, dcdX_stall will be false, thus we can simply |
// do a ... |
assign dcd_stalled = 1'b0; |
`endif |
// |
// PIPELINE STAGE #3 :: Read Operands |
// Calculate stall conditions |
wire op_lock_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) |
||(opvalid)&&((opF_wr)||((opR_wr)&&(opR[3:0] == `CPU_CC_REG))) |
||((alF_wr)||((alu_wr)&&(alu_reg[3:0] == `CPU_CC_REG))) |
||(mem_busy)||(div_busy)||(fpu_busy); |
|
assign op_stall = (opvalid)&&( // 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) |
// |
// Stall if we are going into memory with an operation |
// that cannot be pipelined, and the memory is |
// already busy |
||(mem_stalled) // &&(opvalid_mem) part of mem_stalled |
||(opR_cc) |
) |
||(dcdvalid)&&( |
// Stall if we need to wait for an operand A |
// to be ready to read |
(dcdA_stall) |
// Likewise for B, also includes logic |
// regarding immediate offset (register must |
// be in register file if we need to add to |
// an immediate) |
||(dcdB_stall) |
// Or if we need to wait on flags to work on the |
// CC register |
||(dcdF_stall) |
); |
assign op_ce = ((dcdvalid)||(dcd_illegal))&&(~op_stall)&&(~clear_pipeline); |
|
|
// 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 opvalid is true. |
assign op_change_data_ce = (~op_stall); |
`else |
assign op_stall = (opvalid)&&(~master_ce); |
assign op_ce = ((dcdvalid)||(dcd_illegal))&&(~clear_pipeline); |
assign op_change_data_ce = 1'b1; |
`endif |
|
// |
// 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 |
// is de-selected |
// 3. Stall if someone on the other end is writing the CC register, |
// since we don't know if it'll put us to sleep or not. |
// 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))&&(opvalid_alu)) //Case 1&2 |
||((opvalid)&&(op_lock)&&(op_lock_stall)) |
||((opvalid)&&(op_break)) |
||(wr_reg_ce)&&(wr_write_cc) |
||(div_busy)||(fpu_busy); |
assign alu_ce = (master_ce)&&(opvalid_alu)&&(~alu_stall) |
&&(~clear_pipeline); |
`else |
assign alu_stall = (opvalid_alu)&&((~master_ce)||(op_break)); |
assign alu_ce = (master_ce)&&((opvalid_alu)||(op_illegal))&&(~alu_stall)&&(~clear_pipeline); |
`endif |
// |
|
// |
// Note: if you change the conditions for mem_ce, you must also change |
// alu_pc_valid. |
// |
`ifdef OPT_PIPELINED |
assign mem_ce = (master_ce)&&(opvalid_mem)&&(~mem_stalled) |
&&(~clear_pipeline); |
`else |
// 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. |
// |
// However, in hind sight this logic didn't work. What happens when |
// something gets in the pipeline and then (due to interrupt or some |
// such) needs to be voided? Thus we avoid simplification and keep |
// what worked here. |
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)&&( |
(mem_pipe_stalled) |
||((~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_write_pc)||(wr_write_cc))))); |
`else |
`ifdef OPT_PIPELINED |
assign mem_stalled = (mem_busy)||((opvalid_mem)&&( |
(~master_ce) |
// Stall waiting for flags to be valid |
// Or waiting for a write to the PC register |
// Or CC register, since that can change the |
// PC as well |
||((wr_reg_ce)&&(wr_reg_id[4] == op_gie)&&((wr_write_pc)||(wr_write_cc))))); |
`else |
assign mem_stalled = (opvalid_mem)&&(~master_ce); |
`endif |
`endif |
|
// ALU, DIV, or FPU CE ... equivalent to the OR of all three of these |
assign adf_ce_unconditional = (master_ce)&&(~clear_pipeline)&&(opvalid) |
&&(~opvalid_mem)&&(~mem_rdbusy) |
&&((~opvalid_alu)||(~alu_stall))&&(~op_break) |
&&(~div_busy)&&(~fpu_busy)&&(~clear_pipeline); |
|
// |
// |
// PIPELINE STAGE #1 :: Prefetch |
// |
// |
`ifdef OPT_SINGLE_FETCH |
wire pf_ce; |
|
assign pf_ce = (~pf_valid)&&(~dcdvalid)&&(~opvalid)&&(~alu_busy)&&(~mem_busy)&&(~alu_pc_valid)&&(~mem_pc_valid); |
prefetch #(ADDRESS_WIDTH) |
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, |
pf_ack, pf_stall, pf_err, i_wb_data); |
|
initial r_dcdvalid = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(clear_pipeline)) |
r_dcdvalid <= 1'b0; |
else if (dcd_ce) |
r_dcdvalid <= (pf_valid)||(pf_illegal); |
else if (op_ce) |
r_dcdvalid <= 1'b0; |
assign dcdvalid = r_dcdvalid; |
|
`else // Pipe fetch |
|
`ifdef OPT_TRADITIONAL_PFCACHE |
pfcache #(LGICACHE, ADDRESS_WIDTH) |
pf(i_clk, i_rst, (new_pc)||((dcd_early_branch)&&(~clear_pipeline)), |
w_clear_icache, |
// dcd_pc, |
~dcd_stalled, |
((dcd_early_branch)&&(~clear_pipeline)) |
? dcd_branch_pc:pf_pc, |
instruction, 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); |
`else |
pipefetch #(RESET_ADDRESS, LGICACHE, ADDRESS_WIDTH) |
pf(i_clk, i_rst, (new_pc)||(dcd_early_branch), |
w_clear_icache, ~dcd_stalled, |
(new_pc)?pf_pc:dcd_branch_pc, |
instruction, instruction_pc, pf_valid, |
pf_cyc, pf_stb, pf_we, pf_addr, pf_data, |
pf_ack, pf_stall, pf_err, i_wb_data, |
//`ifdef OPT_PRECLEAR_BUS |
//((dcd_clear_bus)&&(dcdvalid)) |
//||((op_clear_bus)&&(opvalid)) |
//|| |
//`endif |
(mem_cyc_lcl)||(mem_cyc_gbl), |
pf_illegal); |
`endif |
assign instruction_gie = gie; |
|
initial r_dcdvalid = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(clear_pipeline)||(w_clear_icache)) |
r_dcdvalid <= 1'b0; |
else if (dcd_ce) |
r_dcdvalid <= (pf_valid)&&(~dcd_ljmp)&&(~dcd_early_branch); |
else if (op_ce) |
r_dcdvalid <= 1'b0; |
assign dcdvalid = r_dcdvalid; |
`endif |
|
`ifdef OPT_NEW_INSTRUCTION_SET |
|
// If not pipelined, there will be no opvalid_ anything, and the |
idecode #(AW, IMPLEMENT_MPY, EARLY_BRANCHING, IMPLEMENT_DIVIDE, |
IMPLEMENT_FPU) |
instruction_decoder(i_clk, (i_rst)||(clear_pipeline), |
(~dcdvalid)||(~op_stall), dcd_stalled, instruction, instruction_gie, |
instruction_pc, pf_valid, pf_illegal, dcd_phase, |
dcd_illegal, dcd_pc, dcd_gie, |
{ dcdR_cc, dcdR_pc, dcdR }, |
{ dcdA_cc, dcdA_pc, dcdA }, |
{ dcdB_cc, dcdB_pc, dcdB }, |
dcdI, dcd_zI, dcdF, dcdF_wr, dcdOp, |
dcdALU, dcdM, dcdDV, dcdFP, dcd_break, dcd_lock, |
dcdR_wr,dcdA_rd, dcdB_rd, |
dcd_early_branch, |
dcd_branch_pc, dcd_ljmp, |
dcd_pipe); |
`else |
idecode_deprecated |
#(AW, IMPLEMENT_MPY, EARLY_BRANCHING, IMPLEMENT_DIVIDE, |
IMPLEMENT_FPU) |
instruction_decoder(i_clk, (i_rst)||(clear_pipeline), |
dcd_ce, dcd_stalled, instruction, instruction_gie, |
instruction_pc, pf_valid, pf_illegal, dcd_phase, |
dcd_illegal, dcd_pc, dcd_gie, |
{ dcdR_cc, dcdR_pc, dcdR }, |
{ dcdA_cc, dcdA_pc, dcdA }, |
{ dcdB_cc, dcdB_pc, dcdB }, |
dcdI, dcd_zI, dcdF, dcdF_wr, dcdOp, |
dcdALU, dcdM, dcdDV, dcdFP, dcd_break, dcd_lock, |
dcdR_wr,dcdA_rd, dcdB_rd, |
dcd_early_branch, |
dcd_branch_pc, |
dcd_pipe); |
assign dcd_ljmp = 1'b0; |
`endif |
|
`ifdef OPT_PIPELINED_BUS_ACCESS |
reg r_op_pipe; |
|
initial r_op_pipe = 1'b0; |
// To be a pipeable operation, there must be |
// two valid adjacent instructions |
// Both must be memory instructions |
// Both must be writes, or both must be reads |
// Both operations must be to the same identical address, |
// or at least a single (one) increment above that address |
// |
// However ... we need to know this before this clock, hence this is |
// calculated in the instruction decoder. |
always @(posedge i_clk) |
if (op_ce) |
r_op_pipe <= dcd_pipe; |
else if (mem_ce) // Clear us any time an op_ is clocked in |
r_op_pipe <= 1'b0; |
assign op_pipe = r_op_pipe; |
`else |
assign op_pipe = 1'b0; |
`endif |
|
// |
// |
// PIPELINE STAGE #3 :: Read Operands (Registers) |
// |
// |
assign w_opA = regset[dcdA]; |
assign w_opB = regset[dcdB]; |
|
wire [8:0] w_cpu_info; |
assign w_cpu_info = { |
`ifdef OPT_ILLEGAL_INSTRUCTION |
1'b1, |
`else |
1'b0, |
`endif |
`ifdef OPT_MULTIPLY |
1'b1, |
`else |
1'b0, |
`endif |
`ifdef OPT_DIVIDE |
1'b1, |
`else |
1'b0, |
`endif |
`ifdef OPT_IMPLEMENT_FPU |
1'b1, |
`else |
1'b0, |
`endif |
`ifdef OPT_PIPELINED |
1'b1, |
`else |
1'b0, |
`endif |
`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_VLIW |
1'b1 |
`else |
1'b0 |
`endif |
}; |
|
wire [31:0] w_pcA_v; |
generate |
if (AW < 32) |
assign w_pcA_v = {{(32-AW){1'b0}}, (dcdA[4] == dcd_gie)?dcd_pc:upc }; |
else |
assign w_pcA_v = (dcdA[4] == dcd_gie)?dcd_pc:upc; |
endgenerate |
|
`ifdef OPT_PIPELINED |
reg [4:0] opA_id, opB_id; |
reg opA_rd, opB_rd; |
always @(posedge i_clk) |
if (op_ce) |
begin |
opA_id <= dcdA; |
opB_id <= dcdB; |
opA_rd <= dcdA_rd; |
opB_rd <= dcdB_rd; |
end |
`endif |
|
always @(posedge i_clk) |
if (op_change_data_ce) |
begin |
if ((wr_reg_ce)&&(wr_reg_id == dcdA)) |
r_opA <= wr_gpreg_vl; |
else if (dcdA_pc) |
r_opA <= w_pcA_v; |
else if (dcdA_cc) |
r_opA <= { w_cpu_info, w_opA[22:15], (dcdA[4])?w_uflags:w_iflags }; |
else |
r_opA <= w_opA; |
`ifdef OPT_PIPELINED |
end else |
begin // We were going to pick these up when they became valid, |
// but for some reason we're stuck here as they became |
// valid. Pick them up now anyway |
// if (((opA_alu)&&(alu_wr))||((opA_mem)&&(mem_valid))) |
// r_opA <= wr_gpreg_vl; |
if ((wr_reg_ce)&&(wr_reg_id == opA_id)&&(opA_rd)) |
r_opA <= wr_gpreg_vl; |
`endif |
end |
|
wire [31:0] w_opBnI, w_pcB_v; |
generate |
if (AW < 32) |
assign w_pcB_v = {{(32-AW){1'b0}}, (dcdB[4] == dcd_gie)?dcd_pc:upc }; |
else |
assign w_pcB_v = (dcdB[4] == dcd_gie)?dcd_pc:upc; |
endgenerate |
|
assign w_opBnI = (~dcdB_rd) ? 32'h00 |
: (((wr_reg_ce)&&(wr_reg_id == dcdB)) ? wr_gpreg_vl |
: ((dcdB_pc) ? w_pcB_v |
: ((dcdB_cc) ? { w_cpu_info, w_opB[22:15], // w_opB[31:14], |
(dcdB[4])?w_uflags:w_iflags} |
: w_opB))); |
|
always @(posedge i_clk) |
if (op_change_data_ce) |
r_opB <= w_opBnI + dcdI; |
`ifdef OPT_PIPELINED |
else if ((wr_reg_ce)&&(opB_id == wr_reg_id)&&(opB_rd)) |
r_opB <= wr_gpreg_vl; |
`endif |
|
// The logic here has become more complex than it should be, no thanks |
// to Xilinx's Vivado trying to help. The conditions are supposed to |
// be two sets of four bits: the top bits specify what bits matter, the |
// bottom specify what those top bits must equal. However, two of |
// conditions check whether bits are on, and those are the only two |
// conditions checking those bits. Therefore, Vivado complains that |
// these two bits are redundant. Hence the convoluted expression |
// below, arriving at what we finally want in the (now wire net) |
// opF. |
always @(posedge i_clk) |
if (op_ce) // Cannot do op_change_data_ce here since opF 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(dcdF[2:0]) |
3'h0: r_opF <= 6'h00; // Always |
`ifdef OPT_NEW_INSTRUCTION_SET |
// These were remapped as part of the new instruction |
// set in order to make certain that the low order |
// two bits contained the most commonly used |
// conditions: Always, LT, Z, and NZ. |
3'h1: r_opF <= 6'h24; // LT |
3'h2: r_opF <= 6'h11; // Z |
3'h3: r_opF <= 6'h10; // NE |
3'h4: r_opF <= 6'h30; // GT (!N&!Z) |
3'h5: r_opF <= 6'h20; // GE (!N) |
`else |
3'h1: r_opF <= 6'h11; // Z |
3'h2: r_opF <= 6'h10; // NE |
3'h3: r_opF <= 6'h20; // GE (!N) |
3'h4: r_opF <= 6'h30; // GT (!N&!Z) |
3'h5: r_opF <= 6'h24; // LT |
`endif |
3'h6: r_opF <= 6'h02; // C |
3'h7: r_opF <= 6'h08; // V |
endcase |
end // Bit order is { (flags_not_used), VNCZ mask, VNCZ value } |
assign opF = { r_opF[3], r_opF[5], r_opF[1], r_opF[4:0] }; |
|
wire w_opvalid; |
assign w_opvalid = (~clear_pipeline)&&(dcdvalid)&&(~dcd_ljmp); |
initial opvalid = 1'b0; |
initial opvalid_alu = 1'b0; |
initial opvalid_mem = 1'b0; |
initial opvalid_div = 1'b0; |
initial opvalid_fpu = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
begin |
opvalid <= 1'b0; |
opvalid_alu <= 1'b0; |
opvalid_mem <= 1'b0; |
opvalid_div <= 1'b0; |
opvalid_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. |
opvalid<= (w_opvalid)||(dcd_illegal)&&(dcdvalid); |
`ifdef OPT_ILLEGAL_INSTRUCTION |
opvalid_alu <= (w_opvalid)&&((dcdALU)||(dcd_illegal)); |
opvalid_mem <= (dcdM)&&(~dcd_illegal)&&(w_opvalid); |
opvalid_div <= (dcdDV)&&(~dcd_illegal)&&(w_opvalid); |
opvalid_fpu <= (dcdFP)&&(~dcd_illegal)&&(w_opvalid); |
`else |
opvalid_alu <= (dcdALU)&&(w_opvalid); |
opvalid_mem <= (dcdM)&&(w_opvalid); |
opvalid_div <= (dcdDV)&&(w_opvalid); |
opvalid_fpu <= (dcdFP)&&(w_opvalid); |
`endif |
end else if ((clear_pipeline)||(adf_ce_unconditional)||(mem_ce)) |
begin |
opvalid <= 1'b0; |
opvalid_alu <= 1'b0; |
opvalid_mem <= 1'b0; |
opvalid_div <= 1'b0; |
opvalid_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 |
// instruction from entering the ALU, and cause an interrupt before |
// this instruction. Thus, returning to this code will cause the |
// break to repeat and continue upon return. To get out of this |
// condition, replace the break instruction with what it is supposed |
// 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_dcdI[15:0] == 16'h0001); |
initial op_break = 1'b0; |
always @(posedge i_clk) |
if (i_rst) op_break <= 1'b0; |
else if (op_ce) op_break <= (dcd_break); // &&(dcdvalid) |
else if ((clear_pipeline)||(~opvalid)) |
op_break <= 1'b0; |
|
`ifdef OPT_PIPELINED |
generate |
if (IMPLEMENT_LOCK != 0) |
begin |
reg r_op_lock, r_op_lock_stall; |
|
initial r_op_lock_stall = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
r_op_lock_stall <= 1'b0; |
else |
r_op_lock_stall <= (~opvalid)||(~op_lock) |
||(~dcdvalid)||(~pf_valid); |
|
assign op_lock_stall = r_op_lock_stall; |
|
initial r_op_lock = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(clear_pipeline)) |
r_op_lock <= 1'b0; |
else if (op_ce) |
r_op_lock <= (dcd_lock)&&(~clear_pipeline); |
assign op_lock = r_op_lock; |
|
end else begin |
assign op_lock_stall = 1'b0; |
assign op_lock = 1'b0; |
end endgenerate |
|
`else |
assign op_lock_stall = 1'b0; |
assign op_lock = 1'b0; |
`endif |
|
`ifdef OPT_ILLEGAL_INSTRUCTION |
initial op_illegal = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(clear_pipeline)) |
op_illegal <= 1'b0; |
else if(op_ce) |
`ifdef OPT_PIPELINED |
op_illegal <= (dcdvalid)&&((dcd_illegal)||((dcd_lock)&&(IMPLEMENT_LOCK == 0))); |
`else |
op_illegal <= (dcdvalid)&&((dcd_illegal)||(dcd_lock)); |
`endif |
`endif |
else if(alu_ce) |
op_illegal <= 1'b0; |
|
// 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. |
always @(posedge i_clk) |
if (op_ce) |
begin |
opF_wr <= (dcdF_wr)&&((~dcdR_cc)||(~dcdR_wr)) |
&&(~dcd_early_branch)&&(~dcd_illegal); |
opR_wr <= (dcdR_wr)&&(~dcd_early_branch)&&(~dcd_illegal); |
end |
|
always @(posedge i_clk) |
if (op_change_data_ce) |
begin |
opn <= dcdOp; // Which ALU operation? |
// opM <= dcdM; // Is this a memory operation? |
// What register will these results be written into? |
opR <= dcdR; |
opR_cc <= (dcdR_cc)&&(dcdR_wr)&&(dcdR[4]==dcd_gie); |
// User level (1), vs supervisor (0)/interrupts disabled |
op_gie <= dcd_gie; |
|
|
// |
op_pc <= (dcd_early_branch)?dcd_branch_pc:dcd_pc; |
end |
assign opFl = (op_gie)?(w_uflags):(w_iflags); |
|
`ifdef OPT_VLIW |
reg r_op_phase; |
initial r_op_phase = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(clear_pipeline)) |
r_op_phase <= 1'b0; |
else if (op_change_data_ce) |
r_op_phase <= dcd_phase; |
assign op_phase = r_op_phase; |
`else |
assign op_phase = 1'b0; |
`endif |
|
// 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 |
// register with the results of an ALU or memory operation, if such |
// results are now available. Otherwise, we'd need to insert a wait |
// state of some type. |
// |
// The alternative approach would be to define some sort of |
// op_stall wire, which would stall any upstream stage. |
// 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 opA = ((wr_reg_ce)&&(wr_reg_id == opA_id)) // &&(opA_rd)) |
? wr_gpreg_vl : r_opA; |
`else |
assign opA = r_opA; |
`endif |
|
`ifdef OPT_PIPELINED |
// Stall if we have decoded an instruction that will read register A |
// AND ... something that may write a register is running |
// AND (series of conditions here ...) |
// The operation might set flags, and we wish to read the |
// CC register |
// OR ... (No other conditions) |
assign dcdA_stall = (dcdA_rd) // &&(dcdvalid) is checked for elsewhere |
&&((opvalid)||(mem_rdbusy) |
||(div_busy)||(fpu_busy)) |
&&(((opF_wr)||(cc_invalid_for_dcd))&&(dcdA_cc)) |
||((dcdA_rd)&&(dcdA_cc)&&(cc_invalid_for_dcd)); |
`else |
// There are no pipeline hazards, if we aren't pipelined |
assign dcdA_stall = 1'b0; |
`endif |
|
`ifdef OPT_PIPELINED |
assign opB = ((wr_reg_ce)&&(wr_reg_id == opB_id)&&(opB_rd)) |
? wr_gpreg_vl: r_opB; |
`else |
assign opB = r_opB; |
`endif |
|
`ifdef OPT_PIPELINED |
// Stall if we have decoded an instruction that will read register B |
// AND ... something that may write a (unknown) register is running |
// AND (series of conditions here ...) |
// The operation might set flags, and we wish to read the |
// CC register |
// OR the operation might set register B, and we still need |
// a clock to add the offset to it |
assign dcdB_stall = (dcdB_rd) // &&(dcdvalid) 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 |
// (opR_wr, opF_wr, opR) in our logic below. |
&&((opvalid)||(mem_rdbusy) |
||(div_busy)||(fpu_busy)||(alu_busy)) |
&&( |
// Okay, what happens if the result register |
// from instruction 1 becomes the input for |
// instruction two, *and* there's an immediate |
// offset in instruction two? In that case, we |
// need an extra clock between the two |
// instructions to calculate the base plus |
// offset. |
// |
// What if instruction 1 (or before) is in a |
// memory pipeline? We may no longer know what |
// the register was! We will then need to |
// blindly wait. We'll temper this only waiting |
// if we're not piping this new instruction. |
// If we were piping, the pipe logic in the |
// decode circuit has told us that the hazard |
// is clear, so we're okay then. |
// |
((~dcd_zI)&&( |
((opR == dcdB)&&(opR_wr)) |
||((mem_rdbusy)&&(~dcd_pipe)) |
)) |
// Stall following any instruction that will |
// set the flags, if we're going to need the |
// flags (CC) register for opB. |
||(((opF_wr)||(cc_invalid_for_dcd))&&(dcdB_cc)) |
// Stall on any ongoing memory operation that |
// will write to opB -- captured above |
// ||((mem_busy)&&(~mem_we)&&(mem_last_reg==dcdB)&&(~dcd_zI)) |
) |
||((dcdB_rd)&&(dcdB_cc)&&(cc_invalid_for_dcd)); |
assign dcdF_stall = ((~dcdF[3]) |
||((dcdA_rd)&&(dcdA_cc)) |
||((dcdB_rd)&&(dcdB_cc))) |
&&(opvalid)&&(opR_cc); |
// &&(dcdvalid) is checked for elsewhere |
`else |
// No stalls without pipelining, 'cause how can you have a pipeline |
// hazard without the pipeline? |
assign dcdB_stall = 1'b0; |
assign dcdF_stall = 1'b0; |
`endif |
// |
// |
// PIPELINE STAGE #4 :: Apply Instruction |
// |
// |
`ifdef OPT_NEW_INSTRUCTION_SET |
cpuops #(IMPLEMENT_MPY) doalu(i_clk, i_rst, alu_ce, |
(opvalid_alu), opn, opA, opB, |
alu_result, alu_flags, alu_valid, alu_illegal_op, |
alu_busy); |
`else |
cpuops_deprecated #(IMPLEMENT_MPY) doalu(i_clk, i_rst, alu_ce, |
(opvalid_alu), opn, opA, opB, |
alu_result, alu_flags, alu_valid, alu_illegal_op); |
assign alu_busy = 1'b0; |
`endif |
|
generate |
if (IMPLEMENT_DIVIDE != 0) |
begin |
div thedivide(i_clk, (i_rst)||(clear_pipeline), div_ce, opn[0], |
opA, opB, 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; |
end endgenerate |
|
generate |
if (IMPLEMENT_FPU != 0) |
begin |
// |
// sfpu thefpu(i_clk, i_rst, fpu_ce, |
// opA, opB, fpu_busy, fpu_valid, fpu_err, fpu_result, |
// fpu_flags); |
// |
assign fpu_error = 1'b0; // Must only be true if fpu_valid |
assign fpu_busy = 1'b0; |
assign fpu_valid = 1'b0; |
assign fpu_result= 32'h00; |
assign fpu_flags = 4'h0; |
end else begin |
assign fpu_error = 1'b0; |
assign fpu_busy = 1'b0; |
assign fpu_valid = 1'b0; |
assign fpu_result= 32'h00; |
assign fpu_flags = 4'h0; |
end endgenerate |
|
|
assign set_cond = ((opF[7:4]&opFl[3:0])==opF[3:0]); |
initial alF_wr = 1'b0; |
initial alu_wr = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
begin |
alu_wr <= 1'b0; |
alF_wr <= 1'b0; |
end else if (alu_ce) |
begin |
// alu_reg <= opR; |
alu_wr <= (opR_wr)&&(set_cond); |
alF_wr <= (opF_wr)&&(set_cond); |
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); |
alF_wr <= 1'b0; |
end |
|
`ifdef OPT_VLIW |
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) |
if (adf_ce_unconditional) |
alu_reg <= opR; |
else if ((i_halt)&&(i_dbg_we)) |
alu_reg <= i_dbg_reg; |
|
// |
// 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; |
always @(posedge i_clk) |
dbg_val <= i_dbg_data; |
always @(posedge i_clk) |
if ((adf_ce_unconditional)||(mem_ce)) |
alu_gie <= op_gie; |
always @(posedge i_clk) |
if ((adf_ce_unconditional) |
||((master_ce)&&(opvalid_mem)&&(~clear_pipeline) |
&&(~mem_stalled))) |
alu_pc <= op_pc; |
|
`ifdef OPT_ILLEGAL_INSTRUCTION |
reg r_alu_illegal; |
initial r_alu_illegal = 0; |
always @(posedge i_clk) |
if ((i_rst)||(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 = (alu_illegal_op)||(r_alu_illegal); |
`else |
assign alu_illegal = 1'b0; |
`endif |
|
initial r_alu_pc_valid = 1'b0; |
initial mem_pc_valid = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
r_alu_pc_valid <= 1'b0; |
else if (adf_ce_unconditional)//Includes&&(~alu_clear_pipeline) |
r_alu_pc_valid <= 1'b1; |
else if (((~alu_busy)&&(~div_busy)&&(~fpu_busy))||(clear_pipeline)) |
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) |
mem_pc_valid <= 1'b0; |
else |
mem_pc_valid <= (mem_ce); |
|
wire bus_lock; |
`ifdef OPT_PIPELINED |
generate |
if (IMPLEMENT_LOCK != 0) |
begin |
reg [1:0] r_bus_lock; |
initial r_bus_lock = 2'b00; |
always @(posedge i_clk) |
if (i_rst) |
r_bus_lock <= 2'b00; |
else if ((op_ce)&&(op_lock)) |
r_bus_lock <= 2'b11; |
else if ((|r_bus_lock)&&((~opvalid_mem)||(~op_ce))) |
r_bus_lock <= r_bus_lock + 2'b11; |
assign bus_lock = |r_bus_lock; |
end else begin |
assign bus_lock = 1'b0; |
end endgenerate |
`else |
assign bus_lock = 1'b0; |
`endif |
|
`ifdef OPT_PIPELINED_BUS_ACCESS |
pipemem #(AW,IMPLEMENT_LOCK) domem(i_clk, i_rst,(mem_ce)&&(set_cond), bus_lock, |
(opn[0]), opB, opA, opR, |
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_ack, mem_stall, mem_err, i_wb_data); |
|
`else // PIPELINED_BUS_ACCESS |
memops #(AW,IMPLEMENT_LOCK) domem(i_clk, i_rst,(mem_ce)&&(set_cond), bus_lock, |
(opn[0]), opB, opA, opR, |
mem_busy, |
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_ack, mem_stall, mem_err, i_wb_data); |
`endif // PIPELINED_BUS_ACCESS |
assign mem_rdbusy = ((mem_busy)&&(~mem_we)); |
|
// Either the prefetch or the instruction gets the memory bus, but |
// never both. |
wbdblpriarb #(32,AW) pformem(i_clk, i_rst, |
// Memory access to the arbiter, priority position |
mem_cyc_gbl, mem_cyc_lcl, mem_stb_gbl, mem_stb_lcl, |
mem_we, mem_addr, mem_data, mem_ack, mem_stall, mem_err, |
// Prefetch access to the arbiter |
pf_cyc, 1'b0, pf_stb, 1'b0, pf_we, pf_addr, pf_data, |
pf_ack, pf_stall, pf_err, |
// 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, |
i_wb_ack, i_wb_stall, i_wb_err); |
|
|
|
// |
// |
// |
// |
// |
// |
// |
// |
// 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 |
// won't prevent write back, nor debug modes, halting the CPU, nor |
// anything else. Indeed, the (master_ce) bit is only as relevant |
// as knowinig something is available for writeback. |
|
// |
// Write back to our generic register set ... |
// When shall we write back? On one of two conditions |
// Note that the flags needed to be checked before issuing the |
// bus instruction, so they don't need to be checked here. |
// Further, alu_wr includes (set_cond), so we don't need to |
// check for that here either. |
`ifdef OPT_ILLEGAL_INSTRUCTION |
assign wr_reg_ce = (dbgv)||(mem_valid) |
||((~clear_pipeline)&&(~alu_illegal) |
&&(((alu_wr)&&(alu_valid)) |
||(div_valid)||(fpu_valid))); |
`else |
assign wr_reg_ce = (dbgv)||(mem_valid) |
||((~clear_pipeline) |
&&(((alu_wr)&&(alu_valid)) |
||(div_valid)||(fpu_valid))); |
`endif |
// 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. |
assign wr_reg_id = (alu_wr|div_valid|fpu_valid)?alu_reg:mem_wreg; |
// 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}); |
assign wr_write_ucc = (wr_reg_id[4:0] == {1'b1, `CPU_CC_REG}); |
// Are we writing to the PC? |
assign wr_write_pc = (wr_reg_id[3:0] == `CPU_PC_REG); |
|
// What value to write? |
assign wr_gpreg_vl = ((mem_valid) ? mem_result |
:((div_valid|fpu_valid)) |
? ((div_valid) ? div_result:fpu_result) |
:((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) |
regset[wr_reg_id] <= wr_gpreg_vl; |
|
// |
// Write back to the condition codes/flags register ... |
// When shall we write to our flags register? alF_wr already |
// includes the set condition ... |
assign wr_flags_ce = ((alF_wr)||(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, |
((wr_flags_ce)&&(alu_gie))?alu_flags:flags }; |
assign w_iflags = { 1'b0, ihalt_phase, ifpu_err_flag, |
idiv_err_flag, ibus_err_flag, trap, ill_err_i, |
break_en, 1'b0, 1'b0, sleep, |
((wr_flags_ce)&&(~alu_gie))?alu_flags:iflags }; |
|
|
// What value to write? |
always @(posedge i_clk) |
// If explicitly writing the register itself |
if ((wr_reg_ce)&&(wr_write_ucc)) |
flags <= wr_gpreg_vl[3:0]; |
// Otherwise if we're setting the flags from an ALU operation |
else if ((wr_flags_ce)&&(alu_gie)) |
flags <= (div_valid)?div_flags:((fpu_valid)?fpu_flags |
: alu_flags); |
|
always @(posedge i_clk) |
if ((wr_reg_ce)&&(wr_write_scc)) |
iflags <= wr_gpreg_vl[3:0]; |
else if ((wr_flags_ce)&&(~alu_gie)) |
iflags <= (div_valid)?div_flags:((fpu_valid)?fpu_flags |
: alu_flags); |
|
// The 'break' enable bit. This bit can only be set from supervisor |
// mode. It control what the CPU does upon encountering a break |
// instruction. |
// |
// The goal, upon encountering a break is that the CPU should stop and |
// not execute the break instruction, choosing instead to enter into |
// either interrupt mode or halt first. |
// if ((break_en) AND (break_instruction)) // user mode or not |
// HALT CPU |
// else if (break_instruction) // only in user mode |
// set an interrupt flag, set the user break bit, |
// go to supervisor mode, allow supervisor to step the CPU. |
// Upon a CPU halt, any break condition will be reset. The |
// external debugger will then need to deal with whatever |
// condition has taken place. |
initial break_en = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(i_halt)) |
break_en <= 1'b0; |
else if ((wr_reg_ce)&&(wr_write_scc)) |
break_en <= wr_spreg_vl[`CPU_BREAK_BIT]; |
|
initial break_pending = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(clear_pipeline)||(~opvalid)) |
break_pending <= 1'b0; |
else if (op_break) |
break_pending <= (~alu_busy)&&(~div_busy)&&(~fpu_busy)&&(~mem_busy); |
else |
break_pending <= 1'b0; |
|
|
assign o_break = ((break_en)||(~op_gie))&&(break_pending) |
&&(~clear_pipeline) |
||((~alu_gie)&&(bus_err)) |
||((~alu_gie)&&(div_error)) |
||((~alu_gie)&&(fpu_error)) |
||((~alu_gie)&&(alu_illegal)); |
|
// The sleep register. Setting the sleep register causes the CPU to |
// sleep until the next interrupt. Setting the sleep register within |
// interrupt mode causes the processor to halt until a reset. This is |
// a panic/fault halt. The trick is that you cannot be allowed to |
// set the sleep bit and switch to supervisor mode in the same |
// instruction: users are not allowed to halt the CPU. |
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]; |
|
always @(posedge i_clk) |
if ((i_rst)||(w_switch_to_interrupt)) |
step <= 1'b0; |
else if ((wr_reg_ce)&&(~alu_gie)&&(wr_write_ucc)) |
step <= wr_spreg_vl[`CPU_STEP_BIT]; |
else if (((alu_pc_valid)||(mem_pc_valid))&&(step)&&(gie)) |
step <= 1'b0; |
|
// The GIE register. Only interrupts can disable the interrupt register |
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)) |
`ifdef OPT_ILLEGAL_INSTRUCTION |
// On an illegal instruction |
||(alu_illegal) |
`endif |
// 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)) |
); |
assign w_release_from_interrupt = (~gie)&&(~i_interrupt) |
// Then if we write the sCC register |
&&(((wr_reg_ce)&&(wr_spreg_vl[`CPU_GIE_BIT]) |
&&(wr_write_scc)) |
); |
always @(posedge i_clk) |
if (i_rst) |
gie <= 1'b0; |
else if (w_switch_to_interrupt) |
gie <= 1'b0; |
else if (w_release_from_interrupt) |
gie <= 1'b1; |
|
initial trap = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(w_release_from_interrupt)) |
trap <= 1'b0; |
else if ((alu_gie)&&(wr_reg_ce)&&(~wr_spreg_vl[`CPU_GIE_BIT]) |
&&(wr_write_ucc)) // &&(wr_reg_id[4]) implied |
trap <= 1'b1; |
else if ((wr_reg_ce)&&(wr_write_ucc)&&(~alu_gie)) |
trap <= (trap)&&(wr_spreg_vl[`CPU_TRAP_BIT]); |
|
initial ubreak = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(w_release_from_interrupt)) |
ubreak <= 1'b0; |
else if ((op_gie)&&(break_pending)&&(w_switch_to_interrupt)) |
ubreak <= 1'b1; |
else if (((~alu_gie)||(dbgv))&&(wr_reg_ce)&&(wr_write_ucc)) |
ubreak <= (ubreak)&&(wr_spreg_vl[`CPU_BREAK_BIT]); |
|
|
`ifdef OPT_ILLEGAL_INSTRUCTION |
initial ill_err_i = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
ill_err_i <= 1'b0; |
// Only the debug interface can clear this bit |
else if ((dbgv)&&(wr_write_scc)) |
ill_err_i <= (ill_err_i)&&(wr_spreg_vl[`CPU_ILL_BIT]); |
else if ((alu_illegal)&&(~alu_gie)) |
ill_err_i <= 1'b1; |
initial 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)) |
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)) |
ill_err_u <=((ill_err_u)&&(wr_spreg_vl[`CPU_ILL_BIT])); |
else if ((alu_illegal)&&(alu_gie)) |
ill_err_u <= 1'b1; |
`else |
assign ill_err_u = 1'b0; |
assign ill_err_i = 1'b0; |
`endif |
// Supervisor/interrupt bus error flag -- this will crash the CPU if |
// ever set. |
initial ibus_err_flag = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
ibus_err_flag <= 1'b0; |
else if ((dbgv)&&(wr_write_scc)) |
ibus_err_flag <= (ibus_err_flag)&&(wr_spreg_vl[`CPU_BUSERR_BIT]); |
else if ((bus_err)&&(~alu_gie)) |
ibus_err_flag <= 1'b1; |
// User bus error flag -- if ever set, it will cause an interrupt to |
// supervisor mode. |
initial ubus_err_flag = 1'b0; |
always @(posedge i_clk) |
if ((i_rst)||(w_release_from_interrupt)) |
ubus_err_flag <= 1'b0; |
else if (((~alu_gie)||(dbgv))&&(wr_reg_ce)&&(wr_write_ucc)) |
ubus_err_flag <= (ubus_err_flag)&&(wr_spreg_vl[`CPU_BUSERR_BIT]); |
else if ((bus_err)&&(alu_gie)) |
ubus_err_flag <= 1'b1; |
|
generate |
if (IMPLEMENT_DIVIDE != 0) |
begin |
reg r_idiv_err_flag, r_udiv_err_flag; |
|
// Supervisor/interrupt divide (by zero) error flag -- this will |
// crash the CPU if ever set. This bit is thus available for us |
// to be able to tell if/why the CPU crashed. |
initial r_idiv_err_flag = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
r_idiv_err_flag <= 1'b0; |
else if ((dbgv)&&(wr_write_scc)) |
r_idiv_err_flag <= (r_idiv_err_flag)&&(wr_spreg_vl[`CPU_DIVERR_BIT]); |
else if ((div_error)&&(~alu_gie)) |
r_idiv_err_flag <= 1'b1; |
// 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 idiv_err_flag = r_idiv_err_flag; |
assign udiv_err_flag = r_udiv_err_flag; |
end else begin |
assign idiv_err_flag = 1'b0; |
assign udiv_err_flag = 1'b0; |
end endgenerate |
|
generate |
if (IMPLEMENT_FPU !=0) |
begin |
// Supervisor/interrupt floating point error flag -- this will |
// crash the CPU if ever set. |
reg r_ifpu_err_flag, r_ufpu_err_flag; |
initial r_ifpu_err_flag = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
r_ifpu_err_flag <= 1'b0; |
else if ((dbgv)&&(wr_write_scc)) |
r_ifpu_err_flag <= (r_ifpu_err_flag)&&(wr_spreg_vl[`CPU_FPUERR_BIT]); |
else if ((fpu_error)&&(fpu_valid)&&(~alu_gie)) |
r_ifpu_err_flag <= 1'b1; |
// User floating point error flag -- if ever set, it will cause |
// 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)) |
r_ufpu_err_flag <= 1'b0; |
else if (((~alu_gie)||(dbgv))&&(wr_reg_ce) |
&&(wr_write_ucc)) |
r_ufpu_err_flag <= (r_ufpu_err_flag)&&(wr_spreg_vl[`CPU_FPUERR_BIT]); |
else if ((fpu_error)&&(alu_gie)&&(fpu_valid)) |
r_ufpu_err_flag <= 1'b1; |
|
assign ifpu_err_flag = r_ifpu_err_flag; |
assign ufpu_err_flag = r_ufpu_err_flag; |
end else begin |
assign ifpu_err_flag = 1'b0; |
assign ufpu_err_flag = 1'b0; |
end endgenerate |
|
`ifdef OPT_VLIW |
reg r_ihalt_phase, r_uhalt_phase; |
|
initial r_ihalt_phase = 0; |
initial r_uhalt_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; |
always @(posedge i_clk) |
if ((i_rst)||(w_release_from_interrupt)) |
r_uhalt_phase <= 1'b0; |
else if ((alu_gie)&&(alu_pc_valid)) |
r_uhalt_phase <= alu_phase; |
else if ((~alu_gie)&&(wr_reg_ce)&&(wr_write_ucc)) |
r_uhalt_phase <= wr_spreg_vl[`CPU_PHASE_BIT]; |
|
assign ihalt_phase = r_ihalt_phase; |
assign uhalt_phase = r_uhalt_phase; |
`else |
assign ihalt_phase = 1'b0; |
assign uhalt_phase = 1'b0; |
`endif |
|
// |
// Write backs to the PC register, and general increments of it |
// We support two: upc and ipc. If the instruction is normal, |
// we increment upc, if interrupt level we increment ipc. If |
// the instruction writes the PC, we write whichever PC is appropriate. |
// |
// Do we need to all our partial results from the pipeline? |
// 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? |
always @(posedge i_clk) |
if ((wr_reg_ce)&&(wr_reg_id[4])&&(wr_write_pc)) |
upc <= wr_spreg_vl[(AW-1):0]; |
else if ((alu_gie)&& |
(((alu_pc_valid)&&(~clear_pipeline)) |
||(mem_pc_valid))) |
upc <= alu_pc; |
|
always @(posedge i_clk) |
if (i_rst) |
ipc <= RESET_ADDRESS; |
else if ((wr_reg_ce)&&(~wr_reg_id[4])&&(wr_write_pc)) |
ipc <= wr_spreg_vl[(AW-1):0]; |
else if ((~alu_gie)&& |
(((alu_pc_valid)&&(~clear_pipeline)) |
||(mem_pc_valid))) |
ipc <= alu_pc; |
|
always @(posedge i_clk) |
if (i_rst) |
pf_pc <= RESET_ADDRESS; |
else if ((w_switch_to_interrupt)||((~gie)&&(w_clear_icache))) |
pf_pc <= ipc; |
else if ((w_release_from_interrupt)||((gie)&&(w_clear_icache))) |
pf_pc <= upc; |
else if ((wr_reg_ce)&&(wr_reg_id[4] == gie)&&(wr_write_pc)) |
pf_pc <= wr_spreg_vl[(AW-1):0]; |
`ifdef OPT_PIPELINED |
else if ((dcd_early_branch)&&(~clear_pipeline)) |
pf_pc <= dcd_branch_pc + 1; |
else if ((new_pc)||((~dcd_stalled)&&(pf_valid))) |
pf_pc <= pf_pc + {{(AW-1){1'b0}},1'b1}; |
`else |
else if ((alu_gie==gie)&&( |
((alu_pc_valid)&&(~clear_pipeline)) |
||(mem_pc_valid))) |
pf_pc <= alu_pc; |
`endif |
|
initial new_pc = 1'b1; |
always @(posedge i_clk) |
if ((i_rst)||(i_clear_pf_cache)) |
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)) |
new_pc <= 1'b1; |
else |
new_pc <= 1'b0; |
|
`ifdef OPT_PIPELINED |
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; |
assign w_clear_icache = r_clear_icache; |
`else |
assign w_clear_icache = 1'b0; |
`endif |
|
// |
// The debug interface |
generate |
if (AW<32) |
begin |
always @(posedge i_clk) |
begin |
o_dbg_reg <= regset[i_dbg_reg]; |
if (i_dbg_reg[3:0] == `CPU_PC_REG) |
o_dbg_reg <= {{(32-AW){1'b0}},(i_dbg_reg[4])?upc:ipc}; |
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[31:23] <= w_cpu_info; |
o_dbg_reg[`CPU_GIE_BIT] <= gie; |
end |
end |
end else begin |
always @(posedge i_clk) |
begin |
o_dbg_reg <= regset[i_dbg_reg]; |
if (i_dbg_reg[3:0] == `CPU_PC_REG) |
o_dbg_reg <= (i_dbg_reg[4])?upc:ipc; |
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[31:23] <= w_cpu_info; |
o_dbg_reg[`CPU_GIE_BIT] <= gie; |
end |
end |
end endgenerate |
|
always @(posedge i_clk) |
o_dbg_cc <= { o_break, bus_err, gie, sleep }; |
|
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 |
&&((opvalid)||(i_rst)||(dcd_illegal)) |
// Decode stage must be either valid, in reset, or ill |
&&((dcdvalid)||(i_rst)||(pf_illegal))); |
assign o_dbg_stall = ~r_halted; |
|
// |
// |
// Produce accounting outputs: Account for any CPU stalls, so we can |
// later evaluate how well we are doing. |
// |
// |
assign o_op_stall = (master_ce)&&(op_stall); |
assign o_pf_stall = (master_ce)&&(~pf_valid); |
assign o_i_count = (alu_pc_valid)&&(~clear_pipeline); |
|
`ifdef DEBUG_SCOPE |
reg [31:0] r_stack; |
always @(posedge i_clk) |
if ((wr_reg_ce)&&(wr_reg_id == 5'h0d)) |
r_stack <= wr_gpreg_vl; |
reg r_stack_pre, r_stack_post; |
always @(posedge i_clk) |
r_stack_pre <= (r_stack == 32'h03fff); |
always @(posedge i_clk) |
r_stack_post <= (r_stack == 32'h03eeb); |
|
always @(posedge i_clk) |
o_debug <= { |
/* |
o_break, i_wb_err, pf_pc[1:0], |
flags, |
pf_valid, dcdvalid, opvalid, alu_valid, mem_valid, |
op_ce, alu_ce, mem_ce, |
// |
master_ce, opvalid_alu, opvalid_mem, |
// |
alu_stall, mem_busy, op_pipe, mem_pipe_stalled, |
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_gpreg_vl[4:0] |
*/ |
/* |
i_rst, master_ce, (new_pc), |
((dcd_early_branch)&&(dcdvalid)), |
pf_valid, pf_illegal, |
op_ce, dcd_ce, dcdvalid, dcd_stalled, |
pf_cyc, pf_stb, pf_we, pf_ack, pf_stall, pf_err, |
pf_pc[7:0], pf_addr[7:0] |
*/ |
|
(i_wb_err)||(r_stack_post), (gie)||(r_stack_pre), (alu_illegal)||(r_stack_post), |
(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] } |
: { instruction[31:21] }, |
pf_valid, (pf_valid) ? alu_pc[14:0] |
:{ pf_cyc, pf_stb, pf_pc[12:0] } |
|
/* |
i_wb_err, gie, new_pc, dcd_early_branch, // 4 |
pf_valid, pf_cyc, pf_stb, instruction_pc[0], // 4 |
instruction[30:27], // 4 |
dcd_gie, mem_busy, o_wb_gbl_cyc, o_wb_gbl_stb, // 4 |
dcdvalid, |
((dcd_early_branch)&&(~clear_pipeline)) // 15 |
? dcd_branch_pc[14:0]:pf_pc[14:0] |
*/ |
}; |
`endif |
|
/* |
always @(posedge i_clk) |
o_debug <= { |
// External control interaction (4b) |
i_halt, i_rst, i_clear_cache, o_break, |
// Bus interaction (8b) |
pf_cyc,(o_wb_gbl_cyc|o_wb_lcl_cyc), o_wb_gbl_stb, o_wb_lcl_stb, |
o_wb_we, i_wb_ack, i_wb_stall, i_wb_err, |
// PC control (4b) |
gie, new_pc, dcd_early_branch, 1'b0, |
// Our list of pipeline stage values (8b) |
pf_valid, pf_illegal, dcdvalid, opvalid, alu_valid, mem_valid, |
alu_pc_valid, mem_pc_valid, |
// Our list of circuit enables ... (8b) |
(new_pc)||((dcd_early_branch)&&(~clear_pipeline)), |
dcd_ce, op_ce, alu_ce, mem_ce, wr_reg_ce, wr_flags_ce, |
1'b0, |
// Useful PC values (64b) |
((dcd_early_branch)&&(~clear_pipeline)) |
? dcd_branch_pc[15:0]:pf_pc[15:0], |
(gie)?upc[15:0]:ipc[15:0], instruction_pc[15:0], instruction[31:16] }; |
*/ |
|
endmodule |
/rtl/rtcdate.v
0,0 → 1,211
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: rtcdate.v |
// |
// Project: A Wishbone Controlled Real--time Clock Core |
// |
// Purpose: |
// This core provides a real-time date function that can be coupled with |
// a real-time clock. The date provided is in Binary Coded Decimal (bcd) |
// form, and available for reading and writing over the Wishbone Bus. |
// |
// WARNING: Race conditions exist when updating the date across the Wishbone |
// bus at or near midnight. (This should be obvious, but it bears |
// stating.) Specifically, if the update command shows up at the same |
// clock as the ppd clock, then the ppd clock will be ignored and the |
// new date will be the date of the day following midnight. However, |
// if the update command shows up one clock before the ppd, then the date |
// may be updated, but may have problems dealing with the last day of the |
// month or year. To avoid race conditions, update the date sometime |
// after the stroke of midnight and before 5 clocks before the next |
// midnight. If you are concerned that you might hit a race condition, |
// just read the clock again (5+ clocks later) to make certain you set |
// it correctly. |
// |
// |
// 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 rtcdate(i_clk, i_ppd, i_wb_cyc, i_wb_stb, i_wb_we, i_wb_data, |
o_wb_ack, o_wb_stall, o_wb_data); |
input i_clk; |
// A one part per day signal, i.e. basically a clock enable line that |
// controls when the beginning of the day happens. This line should |
// be high on the very last second of any day in order for the rtcdate |
// module to always have the right date. |
input i_ppd; |
// Wishbone inputs |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [31:0] i_wb_data; |
// Wishbone outputs |
output reg o_wb_ack; |
output wire o_wb_stall; |
output wire [31:0] o_wb_data; |
|
|
reg [5:0] r_day; |
reg [4:0] r_mon; |
reg [13:0] r_year; |
|
reg last_day_of_month, last_day_of_year, is_leap_year; |
reg [5:0] days_per_month; |
initial days_per_month = 6'h31; // Remember, this is BCD |
always @(posedge i_clk) |
begin // Clock 3 |
case(r_mon) |
5'h01: days_per_month <= 6'h31; // Jan |
5'h02: days_per_month <= (is_leap_year)? 6'h29:6'h28; |
5'h03: days_per_month <= 6'h31; // March |
5'h04: days_per_month <= 6'h30; // April |
5'h05: days_per_month <= 6'h31; // May |
5'h06: days_per_month <= 6'h30; // June |
5'h07: days_per_month <= 6'h31; // July |
5'h08: days_per_month <= 6'h31; // August |
5'h09: days_per_month <= 6'h30; // Sept |
5'h10: days_per_month <= 6'h31; // October |
5'h11: days_per_month <= 6'h30; // November |
5'h12: days_per_month <= 6'h31; // December |
default: days_per_month <= 6'h31; // Invalid month |
endcase |
end |
initial last_day_of_month = 1'b0; |
always @(posedge i_clk) // Clock 4 |
last_day_of_month <= (r_day >= days_per_month); |
initial last_day_of_year = 1'b0; |
always @(posedge i_clk) // Clock 5 |
last_day_of_year <= (last_day_of_month) && (r_mon == 5'h12); |
|
reg year_divisible_by_four, century_year, four_century_year; |
always @(posedge i_clk) // Clock 1 |
year_divisible_by_four<= ((~r_year[0])&&(r_year[4]==r_year[1])); |
always @(posedge i_clk) // Clock 1 |
century_year <= (r_year[7:0] == 8'h00); |
always @(posedge i_clk) // Clock 1 |
four_century_year <= ((~r_year[8])&&((r_year[12]==r_year[9]))); |
always @(posedge i_clk) // Clock 2 |
is_leap_year <= (year_divisible_by_four)&&((~century_year) |
||((century_year)&&(four_century_year))); |
|
|
// Adjust the day of month |
reg [5:0] next_day, fixd_day; |
always @(posedge i_clk) |
if (last_day_of_month) |
next_day <= 6'h01; |
else if (r_day[3:0] != 4'h9) |
next_day <= { r_day[5:4], (r_day[3:0]+4'h1) }; |
else |
next_day <= { (r_day[5:4]+2'h1), 4'h0 }; |
always @(posedge i_clk) |
if ((r_day == 0)||(r_day > 6'h31)||(r_day[3:0] > 4'h9)) |
fixd_day <= 6'h01; |
else |
fixd_day <= r_day; |
|
initial r_day = 6'h01; |
always @(posedge i_clk) |
begin // Depends upon 9 inputs |
if (i_ppd) |
r_day <= next_day; |
else if (~o_wb_ack) |
r_day <= fixd_day; |
|
if ((i_wb_stb)&&(i_wb_we)&&(~i_wb_data[7])) |
r_day <= i_wb_data[5:0]; |
end |
|
// Adjust the month of the year |
reg [4:0] next_mon, fixd_mon; |
always @(posedge i_clk) |
if (last_day_of_year) |
next_mon <= 5'h01; |
else if ((last_day_of_month)&&(r_mon[3:0] != 4'h9)) |
next_mon <= { r_mon[4], (r_mon[3:0] + 4'h1) }; |
else if (last_day_of_month) |
begin |
next_mon[3:0] <= 4'h0; |
next_mon[4] <= 1; |
end else |
next_mon <= r_mon; |
|
always @(posedge i_clk) |
if ((r_mon == 0)||(r_mon > 5'h12)||(r_mon[3:0] > 4'h9)) |
fixd_mon <= 5'h01; |
else |
fixd_mon <= r_mon; |
initial r_mon = 5'h01; |
always @(posedge i_clk) |
begin // Depeds upon 9 inputs |
if (i_ppd) |
r_mon <= next_mon; |
else if (~o_wb_ack) |
r_mon <= fixd_mon; |
|
if ((i_wb_stb)&&(i_wb_we)&&(~i_wb_data[15])) |
r_mon <= i_wb_data[12:8]; |
end |
|
// Adjust the year |
reg [13:0] next_year; |
reg [2:0] next_year_c; |
always @(posedge i_clk) |
begin // Takes 5 clocks to propagate |
next_year_c[0] <= (r_year[ 3: 0]>=4'h9); |
next_year_c[1] <= (r_year[ 7: 4]>4'h9)||((r_year[ 7: 4]==4'h9)&&(next_year_c[0])); |
next_year_c[2] <= (r_year[11: 8]>4'h9)||((r_year[11: 8]==4'h9)&&(next_year_c[1])); |
next_year[ 3: 0] <= (next_year_c[0])? 4'h0:(r_year[ 3: 0]+4'h1); |
next_year[ 7: 4] <= (next_year_c[1])? 4'h0: |
(next_year_c[0])?(r_year[ 7: 4]+4'h1) |
: (r_year[7:4]); |
next_year[11: 8] <= (next_year_c[2])? 4'h0: |
(next_year_c[1])?(r_year[11: 8]+4'h1) |
: (r_year[11: 8]); |
next_year[13:12] <= (next_year_c[2])?(r_year[13:12]+2'h1):r_year[13:12]; |
end |
|
initial r_year = 14'h2000; |
always @(posedge i_clk) |
begin // 11 inputs |
// Deal with any out of bounds conditions |
if (r_year[3:0] > 4'h9) |
r_year[3:0] <= 4'h0; |
if (r_year[7:4] > 4'h9) |
r_year[7:4] <= 4'h0; |
if (r_year[11:8] > 4'h9) |
r_year[11:8] <= 4'h0; |
if ((i_ppd)&&(last_day_of_year)) |
r_year <= next_year; |
|
if ((i_wb_stb)&&(i_wb_we)&&(~i_wb_data[31])) |
r_year <= i_wb_data[29:16]; |
end |
|
always @(posedge i_clk) |
o_wb_ack <= (i_wb_stb); |
assign o_wb_stall = 1'b0; |
assign o_wb_data = { 2'h0, r_year, 3'h0, r_mon, 2'h0, r_day }; |
endmodule |
/rtl/xioddr.v
0,0 → 1,76
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: xioddr.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: For the DDR3 SDRAM, this handles the Xilinx specific portions |
// of the IO necessary to make this happen for one pin only. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 xioddr(i_clk, i_oe, i_v, o_v, io_w); |
input i_clk, i_oe; |
input [1:0] i_v; |
output [1:0] o_v; |
inout io_w; |
|
wire w_internal; |
|
ODDR #( |
.DDR_CLK_EDGE("OPPOSITE_EDGE"), |
.INIT(1'b0), |
.SRTYPE("SYNC") |
) ODDRi( |
.Q(w_internal), |
.C(i_clk), |
.CE(1'b1), |
.D1(i_v[0]), |
.D2(i_v[1]), |
.R(1'b0), |
.S(1'b0)); |
|
IDDR #( |
.DDR_CLK_EDGE("OPPOSITE_EDGE"), |
.INIT_Q1(1'b0), |
.INIT_Q2(1'b0), |
.SRTYPE("SYNC") |
) IDDRi( |
.Q1(o_v[0]), |
.Q2(o_v[1]), |
.C(i_clk), |
.CE(1'b1), |
.D(io_w), |
.R(1'b0), |
.S(1'b0)); |
|
assign io_w = (i_oe) ? w_internal:1'bz; |
|
endmodule |
/rtl/wbddrsdram.v
0,0 → 1,102
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbddrsdram.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 wbddrsdram(i_clk_200mhz, |
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, |
o_wb_ack, o_wb_stb, o_wb_data, |
o_ddr_reset_n, o_ddr_cke, |
o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n, |
o_ddr_dqs, |
o_ddr_addr, o_ddr_ba, o_ddr_data, i_ddr_data); |
input i_clk_200mhz; |
// Wishbone inputs |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [25:0] i_wb_addr; |
input [31:0] i_wb_data; |
// Wishbone outputs |
output reg o_wb_ack; |
output reg o_wb_stall; |
output reg [31:0] o_wb_data; |
// DDR3 RAM Controller |
output wire o_ddr_reset_n; |
output wire o_ddr_reset_cke; |
// Control outputs |
output reg o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n,o_ddr_we_n; |
// DQS outputs:set to 3'b010 when data is active, 3'b100 (i.e. 2'bzz) ow |
output reg [2:0] o_ddr_dqs; |
// Address outputs |
output reg [13:0] o_ddr_addr; |
output reg [2:0] o_ddr_ba; |
// And the data inputs and outputs |
output reg [31:0] o_ddr_data; |
input i_ddr_data; |
|
// |
// tWTR = 7.5 |
// tRRD = 7.5 |
// tREFI= 7.8 |
// tFAW = 45 |
// tRTP = 7.5 |
// tCKE = 5.625 |
// tRFC = 160 |
// tRP = 13.5 |
// tRAS = 36 |
// tRCD = 13.5 |
// |
// RESET: |
// 1. Hold o_reset_n = 1'b0; for 200 us, or 40,000 clocks (65536 perhaps?) |
// Hold cke low during this time as well |
// The clock should be free running into the chip during this time |
// Leave command in NOOP state: {cs,ras,cas,we} = 4'h7; |
// ODT must be held low |
// 2. Hold cke low for another 500us, or 100,000 clocks |
// 3. Raise CKE, continue outputting a NOOP for |
// tXPR, tDLLk, and tZQInit |
// 4. Load MRS2, wait tMRD |
// 4. Load MRS3, wait tMRD |
// 4. Load MRS1, wait tMOD |
// Before using the SDRAM, we'll need to program at least 3 of the mode |
// registers, if not all 4. |
// tMOD clocks are required to program the mode registers, during which |
// time the RAM must be idle. |
// |
// NOOP: CS low, RAS, CAS, and WE high |
|
// Need to set o_wb_dqs high one clock prior to any read. |
// As per spec, ODT = 0 during reads |
assign o_ddr_odt = ~o_ddr_we_n; |
endmodule |
/rtl/gpsclock_tb.v
0,0 → 1,137
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: gpsclock_tb.v |
// |
// Project: A GPS Schooled Clock Core |
// |
// Purpose: Provide a test bench, internal to an FPGA, whereby the GPS |
// clock module can be tested. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 gpsclock_tb(i_clk, i_lcl_pps, o_pps, |
i_wb_cyc_stb, i_wb_we, i_wb_addr, i_wb_data, |
o_wb_ack, o_wb_stall, o_wb_data, |
i_err, i_count, i_step); |
parameter DW=32, RW=64; |
input i_clk, i_lcl_pps; |
output reg o_pps; // To our local circuitry |
// Wishbone Configuration interface |
input i_wb_cyc_stb, i_wb_we; |
input [2:0] i_wb_addr; |
input [(DW-1):0] i_wb_data; |
output reg o_wb_ack; |
output wire o_wb_stall; |
output reg [(DW-1):0] o_wb_data; |
// Status and timing outputs |
input [(RW-1):0] i_err, // Fraction of a second err |
i_count, // Fraction of a second |
i_step; // 2^RW / clock speed (in Hz) |
|
|
reg [31:0] r_jump, r_maxcount; |
reg r_halt; |
|
|
// |
// |
// |
// Wishbone access ... |
// |
// |
// |
always @(posedge i_clk) |
if ((i_wb_cyc_stb)&&(i_wb_we)) |
begin |
case(i_wb_addr) |
3'b000: r_maxcount <= i_wb_data; |
3'b001: r_jump <= i_wb_data; |
// 2'b11: r_def_step <= i_wb_data; |
default: begin end |
// r_defstep <= i_wb_data; |
endcase |
end else |
r_jump <= 32'h00; |
|
reg [31:0] r_err, r_lcl; |
reg [63:0] r_count, r_step; |
|
initial r_lcl = 32'h000; |
initial r_halt = 1'b0; |
always @(posedge i_clk) |
case (i_wb_addr) |
3'b000: o_wb_data <= r_maxcount; |
3'b001: begin o_wb_data <= r_lcl; r_halt <= 1'b1; end // { 31'h00, r_halt }; |
3'b010: begin o_wb_data <= i_err[63:32]; r_halt <= 1'b1; end |
3'b011: o_wb_data <= r_err[31:0]; |
3'b100: o_wb_data <= r_count[63:32]; |
3'b101: o_wb_data <= r_count[31:0]; |
3'b110: o_wb_data <= r_step[63:32]; |
3'b111: begin o_wb_data <= r_step[31:0]; r_halt <= 1'b0; end |
default: o_wb_data <= 0; |
endcase |
|
always @(posedge i_clk) |
o_wb_ack <= i_wb_cyc_stb; |
assign o_wb_stall = 1'b0; |
|
|
// |
// |
// Generate a PPS signal |
// |
// |
reg [31:0] r_ctr; |
always @(posedge i_clk) |
if (r_ctr >= r_maxcount-1) |
r_ctr <= r_ctr+1-r_maxcount+r_jump; |
else |
r_ctr <= r_ctr+1+r_jump; |
always @(posedge i_clk) |
if (r_ctr >= r_maxcount-1) |
o_pps <= 1'b1; |
else |
o_pps <= 1'b0; |
|
reg [31:0] lcl_counter; |
always @(posedge i_clk) |
lcl_counter <= lcl_counter + 32'h001; |
|
always @(posedge i_clk) |
if ((~r_halt)&&(i_lcl_pps)) |
begin |
r_err <= i_err[31:0]; |
r_count <= i_count; |
r_step <= i_step; |
r_lcl <= lcl_counter; |
end |
|
|
endmodule |
|
/rtl/toplevel.v
0,0 → 1,459
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: toplevel.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: This is the top level Verilog file. It is to be contrasted |
// with the other top level Verilog file in this same project in |
// that *this* top level is designed to create a *safe*, low-speed |
// (100MHz), configuration that can be used to test peripherals and other |
// things on the way to building a full featured high speed configuration. |
// |
// Differences between this file and fasttop.v should be limited to speed |
// related differences (such as the number of counts per UART baud), and |
// the different daughter module: fastmaster.v (for 200MHz designs) vs |
// busmaster.v (for 100MHz designs). |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 toplevel(i_clk_100mhz, i_reset_btn, |
i_sw, // Switches |
i_btn, // Buttons |
o_led, // Single color LEDs |
o_clr_led0, o_clr_led1, o_clr_led2, o_clr_led3, // Color LEDs |
// RS232 UART |
i_uart_rx, o_uart_tx, |
// Quad-SPI Flash control |
o_qspi_sck, o_qspi_cs_n, io_qspi_dat, |
// Missing: Ethernet |
o_eth_mdclk, io_eth_mdio, |
// Memory |
o_ddr_reset_n, o_ddr_cke, o_ddr_ck_p, o_ddr_ck_n, |
o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n, |
io_ddr_dqs_p, io_ddr_dqs_n, |
o_ddr_addr, o_ddr_ba, |
io_ddr_data, o_ddr_dm, o_ddr_odt, |
// SD Card |
o_sd_sck, io_sd_cmd, io_sd, i_sd_cs, i_sd_wp, |
// GPS Pmod |
i_gps_pps, i_gps_3df, i_gps_rx, o_gps_tx, |
// OLED Pmod |
o_oled_sck, o_oled_cs_n, o_oled_mosi, o_oled_dcn, o_oled_reset_n, |
o_oled_vccen, o_oled_pmoden, |
// PMod I/O |
i_aux_rx, i_aux_rts, o_aux_tx, o_aux_cts |
); |
input i_clk_100mhz, i_reset_btn; |
input [3:0] i_sw; // Switches |
input [3:0] i_btn; // Buttons |
output wire [3:0] o_led; // LED |
output wire [2:0] o_clr_led0, o_clr_led1, o_clr_led2, o_clr_led3; |
// UARTs |
input i_uart_rx; |
output wire o_uart_tx; |
// Quad SPI flash |
output wire o_qspi_sck, o_qspi_cs_n; |
inout [3:0] io_qspi_dat; |
// Ethernet // Not yet implemented |
// Ethernet control (MDIO) |
output wire o_eth_mdclk; |
inout wire io_eth_mdio; |
// DDR3 SDRAM |
output wire o_ddr_reset_n; |
output wire o_ddr_cke; |
output wire o_ddr_ck_p, o_ddr_ck_n; |
output wire o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n; |
inout [1:0] io_ddr_dqs_p, io_ddr_dqs_n; |
output wire [13:0] o_ddr_addr; |
output wire [2:0] o_ddr_ba; |
inout [15:0] io_ddr_data; |
// |
output wire [1:0] o_ddr_dm; |
output wire o_ddr_odt; |
// SD Card |
output wire o_sd_sck; |
inout io_sd_cmd; |
inout [3:0] io_sd; |
input i_sd_cs; |
input i_sd_wp; |
// GPS PMod |
input i_gps_pps, i_gps_3df, i_gps_rx; |
output wire o_gps_tx; |
// OLEDRGB PMod |
output wire o_oled_sck, o_oled_cs_n, o_oled_mosi, |
o_oled_dcn, o_oled_reset_n, o_oled_vccen, |
o_oled_pmoden; |
// Aux UART |
input i_aux_rx, i_aux_rts; |
output wire o_aux_tx, o_aux_cts; |
|
// Build our master clock |
wire i_clk, clk_for_ddr, clk2_unused, enet_clk, clk5_unused, |
clk_feedback, clk_locked; |
PLLE2_BASE #( |
.BANDWIDTH("OPTIMIZED"), // OPTIMIZED, HIGH, LOW |
.CLKFBOUT_PHASE(0.0), // Phase offset in degrees of CLKFB, (-360-360) |
.CLKIN1_PERIOD(10.0), // Input clock period in ns to ps resolution |
// CLKOUT0_DIVIDE - CLKOUT5_DIVIDE: divide amount for each CLKOUT(1-128) |
.CLKFBOUT_MULT(8), // Multiply value for all CLKOUT (2-64) |
.CLKOUT0_DIVIDE(8), // 100 MHz (Main clock) |
.CLKOUT1_DIVIDE(8), // 100 MHz (DDR3 SDRAM clock) |
.CLKOUT2_DIVIDE(16), // 50 MHz (Flash clock, should we need it) |
.CLKOUT3_DIVIDE(32), // 25 MHz (Ethernet clock ?) |
.CLKOUT4_DIVIDE(16), // 50 MHz (Unused clock?) |
.CLKOUT5_DIVIDE(24), |
// CLKOUT0_DUTY_CYCLE -- Duty cycle for each CLKOUT |
.CLKOUT0_DUTY_CYCLE(0.5), |
.CLKOUT1_DUTY_CYCLE(0.5), |
.CLKOUT2_DUTY_CYCLE(0.5), |
.CLKOUT3_DUTY_CYCLE(0.5), |
.CLKOUT4_DUTY_CYCLE(0.5), |
.CLKOUT5_DUTY_CYCLE(0.5), |
// CLKOUT0_PHASE -- phase offset for each CLKOUT |
.CLKOUT0_PHASE(0.0), |
.CLKOUT1_PHASE(90.0), |
.CLKOUT2_PHASE(0.0), |
.CLKOUT3_PHASE(0.0), |
.CLKOUT4_PHASE(0.0), |
.CLKOUT5_PHASE(0.0), |
.DIVCLK_DIVIDE(1), // Master division value , (1-56) |
.REF_JITTER1(0.0), // Reference input jitter in UI (0.000-0.999) |
.STARTUP_WAIT("FALSE") // Delayu DONE until PLL Locks, ("TRUE"/"FALSE") |
) genclock( |
// Clock outputs: 1-bit (each) output |
.CLKOUT0(i_clk), |
.CLKOUT1(clk_for_ddr), |
.CLKOUT2(clk2_unused), // Reserved for flash, should we need it |
.CLKOUT3(enet_clk), |
.CLKOUT4(clk4_unused), |
.CLKOUT5(clk5_unused), |
.CLKFBOUT(clk_feedback), // 1-bit output, feedback clock |
.LOCKED(clk_locked), |
.CLKIN1(i_clk_100mhz), |
.PWRDWN(1'b0), |
.RST(1'b0), |
.CLKFBIN(clk_feedback) // 1-bit input, feedback clock |
); |
|
// UART interface |
wire [29:0] bus_uart_setup; |
assign bus_uart_setup = 30'h10000019; // 4MBaud, 7 bits |
|
wire [7:0] rx_data, tx_data; |
wire rx_break, rx_parity_err, rx_frame_err, rx_stb; |
wire tx_stb, tx_busy; |
|
reg pwr_reset, pre_reset; |
initial pwr_reset = 1'b1; |
initial pre_reset = 1'b0; |
always @(posedge i_clk) |
pre_reset <= ~i_reset_btn; |
always @(posedge i_clk) |
pwr_reset <= pre_reset; |
|
wire w_ck_uart, w_uart_tx; |
rxuart rcv(i_clk, pwr_reset, bus_uart_setup, i_uart_rx, |
rx_stb, rx_data, rx_break, |
rx_parity_err, rx_frame_err, w_ck_uart); |
txuart txv(i_clk, pwr_reset, bus_uart_setup, 1'b0, |
tx_stb, tx_data, o_uart_tx, tx_busy); |
|
|
|
|
|
|
////// |
// |
// |
// The WB bus interconnect, herein called busmaster, which handles |
// just about ... everything. It is in contrast to the other WB bus |
// interconnect, fastmaster, in that the busmaster build permits |
// peripherals that can *only* operate at 100MHz, no faster. |
// |
// |
////// |
wire w_qspi_sck; |
wire [1:0] qspi_bmod; |
wire [3:0] qspi_dat; |
wire [3:0] i_qspi_dat; |
|
// |
wire [2:0] w_ddr_dqs; |
wire [31:0] wo_ddr_data, wi_ddr_data; |
// |
wire w_mdio, w_mdwe; |
// |
wire w_sd_cmd; |
wire [3:0] w_sd_data; |
busmaster wbbus(i_clk, pwr_reset, |
// External USB-UART bus control |
rx_stb, rx_data, tx_stb, tx_data, tx_busy, |
// Board lights and switches |
i_sw, i_btn, o_led, |
o_clr_led0, o_clr_led1, o_clr_led2, o_clr_led3, |
// Board level PMod I/O |
i_aux_rx, o_aux_tx, o_aux_cts, i_gps_rx, o_gps_tx, |
// Quad SPI flash |
o_qspi_cs_n, w_qspi_sck, qspi_dat, io_qspi_dat, qspi_bmod, |
// DDR3 SDRAM |
o_ddr_reset_n, o_ddr_cke, |
o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n, |
w_ddr_dqs, o_ddr_addr, o_ddr_ba, wo_ddr_data, wi_ddr_data, |
// SD Card |
o_sd_sck, w_sd_cmd, w_sd_data, io_sd_cmd, io_sd, i_sd_cs, |
// Ethernet control (MDIO) lines |
o_eth_mdclk, w_mdio, w_mdwe, io_eth_mdio, |
// OLEDRGB PMod wires |
o_oled_sck, o_oled_cs_n, o_oled_mosi, o_oled_dcn, |
o_oled_reset_n, o_oled_vccen, o_oled_pmoden, |
// GPS PMod |
i_gps_pps, i_gps_3df |
); |
|
////// |
// |
// |
// The rest of this file *should* be identical to fasttop.v. Any |
// differences should be worked out with meld or some such program |
// to keep them to a minimum. |
// |
// |
// Some wires need special treatment, and so are not quite completely |
// handled by the bus master. These are handled below. |
// |
// |
////// |
|
// |
// |
// QSPI)BMOD, Quad SPI bus mode, Bus modes are: |
// 0? Normal serial mode, one bit in one bit out |
// 10 Quad SPI mode, going out |
// 11 Quad SPI mode coming from the device (read mode) |
// |
// ?? Dual mode in (not yet) |
// ?? Dual mode out (not yet) |
// |
// |
// assign io_qspi_dat = (~qspi_bmod[1])?({2'b11,1'bz,qspi_dat[0]}) |
// :((qspi_bmod[0])?(4'bzzzz):(qspi_dat[3:0])); |
// assign i_qspi_dat = io_qspi_dat; |
// |
wire [3:0] i_qspi_dat_ign; |
ODDR #(.DDR_CLK_EDGE("OPPOSITE_EDGE"), .INIT(1'b1), .SRTYPE("SYNC")) |
qsck( |
.Q(o_qspi_sck), |
.C(i_clk), |
.CE(1'b1), |
.D1(w_qspi_sck), |
.D2(w_qspi_sck), |
.R(1'b0), .S(1'b0)); |
xioddr qd0(i_clk, (~qspi_bmod[1])|(~qspi_bmod[0]), |
{ qspi_dat[0], qspi_dat[0] }, |
{ i_qspi_dat_ign[0], i_qspi_dat[0] }, io_qspi_dat[0]); |
xioddr qd1(i_clk, (qspi_bmod == 2'b10), |
{ qspi_dat[1], qspi_dat[1] }, |
{ i_qspi_dat_ign[1], i_qspi_dat[1] }, io_qspi_dat[1]); |
xioddr qd2(i_clk, (~qspi_bmod[1])||(~qspi_bmod[0]), |
{ qspi_dat[2], qspi_dat[2] }, |
{ i_qspi_dat_ign[2], i_qspi_dat[2] }, io_qspi_dat[2]); |
xioddr qd3(i_clk, (~qspi_bmod[1])||(~qspi_bmod[0]), |
{ qspi_dat[3], qspi_dat[3] }, |
{ i_qspi_dat_ign[3], i_qspi_dat[3] }, io_qspi_dat[3]); |
|
// |
// Proposed QSPI mode select, to allow dual I/O mode |
// 000 Normal SPI mode |
// 001 Dual mode input |
// 010 Dual mode, output |
// 101 Quad I/O mode input |
// 110 Quad I/O mode output |
// |
// |
// assign io_qspi_dat[3:2] = (~qspi_bmod[2]) ? 2'b11 |
// : (qspi_bmod[0])?2'bzz : qspi_dat[3:2]; |
// assign io_qspi_dat[1] = (~qspi_bmod[1])?qspi_dat[1]:1'bz; |
// assign io_qspi_dat[0] = (qspi_bmod[0])?1'bz : qspi_dat[0]; |
|
// |
// |
// The following primitive is necessary in order to gain access |
// to the o_qspi_sck pin. |
// |
// |
/* |
wire [3:0] su_nc; // Startup primitive, no connect |
STARTUPE2 #( |
// Leave PROG_USR false to avoid activating the program |
// event security feature. Notes state that such a feature |
// requires encrypted bitstreams. |
.PROG_USR("FALSE"), |
// Sets the configuration clock frequency (in ns) for |
// simulation. |
.SIM_CCLK_FREQ(0.0) |
) STARTUPE2_inst ( |
// CFGCLK, 1'b output: Configuration main clock output -- no connect |
.CFGCLK(su_nc[0]), |
// CFGMCLK, 1'b output: Configuration internal oscillator clock output |
.CFGMCLK(su_nc[1]), |
// EOS, 1'b output: Active high output indicating the End Of Startup. |
.EOS(su_nc[2]), |
// PREQ, 1'b output: PROGRAM request to fabric output |
// Only enabled if PROG_USR is set. This lets the fabric know |
// that a request has been made (either JTAG or pin pulled low) |
// to program the device |
.PREQ(su_nc[3]), |
// CLK, 1'b input: User start-up clock input |
.CLK(1'b0), |
// GSR, 1'b input: Global Set/Reset input |
.GSR(1'b0), |
// GTS, 1'b input: Global 3-state input |
.GTS(1'b0), |
// KEYCLEARB, 1'b input: Clear AES Decrypter Key input from BBRAM |
.KEYCLEARB(1'b0), |
// PACK, 1-bit input: PROGRAM acknowledge input |
// This pin is only enabled if PROG_USR is set. This allows the |
// FPGA to acknowledge a request for reprogram to allow the FPGA |
// to get itself into a reprogrammable state first. |
.PACK(1'b0), |
// USRCLKO, 1-bit input: User CCLK input -- This is why I am using this |
// module at all. |
.USRCCLKO(qspi_sck), |
// USRCCLKTS, 1'b input: User CCLK 3-state enable input |
// An active high here places the clock into a high impedence |
// state. Since we wish to use the clock as an active output |
// always, we drive this pin low. |
.USRCCLKTS(1'b0), |
// USRDONEO, 1'b input: User DONE pin output control |
// Set this to "high" to make sure that the DONE LED pin is |
// high. |
.USRDONEO(1'b1), |
// USRDONETS, 1'b input: User DONE 3-state enable output |
// This enables the FPGA DONE pin to be active. Setting this |
// active high sets the DONE pin to high impedence, setting it |
// low allows the output of this pin to be as stated above. |
.USRDONETS(1'b1) |
); |
*/ |
|
|
|
// |
// |
// Wires for setting up the SD Card Controller |
// |
// |
assign io_sd_cmd = w_sd_cmd ? 1'bz:1'b0; |
assign io_sd[0] = w_sd_data[0]? 1'bz:1'b0; |
assign io_sd[1] = w_sd_data[1]? 1'bz:1'b0; |
assign io_sd[2] = w_sd_data[2]? 1'bz:1'b0; |
assign io_sd[3] = w_sd_data[3]? 1'bz:1'b0; |
assign o_sd_wp = 1'b0; |
|
|
// |
// |
// Wire(s) for setting up the MDIO ethernet control structure |
// |
// |
assign io_eth_mdio = (w_mdwe)?w_mdio : 1'bz; |
|
// |
// |
// Wires for setting up the DDR3 memory |
// |
// |
wire [31:0] r_ddr_data; |
|
xioddr p0(i_clk, ~o_ddr_we_n, { wo_ddr_data[16], wo_ddr_data[0] }, |
{ wi_ddr_data[16], wi_ddr_data[0] }, io_ddr_data[0]); |
|
xioddr p1(i_clk, ~o_ddr_we_n, { wo_ddr_data[17], wo_ddr_data[1] }, |
{ wi_ddr_data[17], wi_ddr_data[1] }, io_ddr_data[1]); |
|
xioddr p2(i_clk, ~o_ddr_we_n, { wo_ddr_data[18], wo_ddr_data[2] }, |
{ wi_ddr_data[18], wi_ddr_data[2] }, io_ddr_data[2]); |
|
xioddr p3(i_clk, ~o_ddr_we_n, { wo_ddr_data[19], wo_ddr_data[3] }, |
{ wi_ddr_data[19], wi_ddr_data[3] }, io_ddr_data[3]); |
|
xioddr p4(i_clk, ~o_ddr_we_n, { wo_ddr_data[20], wo_ddr_data[4] }, |
{ wi_ddr_data[20], wi_ddr_data[4] }, io_ddr_data[4]); |
|
xioddr p5(i_clk, ~o_ddr_we_n, { wo_ddr_data[21], wo_ddr_data[5] }, |
{ wi_ddr_data[21], wi_ddr_data[5] }, io_ddr_data[5]); |
|
xioddr p6(i_clk, ~o_ddr_we_n, { wo_ddr_data[22], wo_ddr_data[6] }, |
{ wi_ddr_data[22], wi_ddr_data[6] }, io_ddr_data[6]); |
|
xioddr p7(i_clk, ~o_ddr_we_n, { wo_ddr_data[23], wo_ddr_data[7] }, |
{ wi_ddr_data[23], wi_ddr_data[7] }, io_ddr_data[7]); |
|
xioddr p8(i_clk, ~o_ddr_we_n, { wo_ddr_data[24], wo_ddr_data[8] }, |
{ wi_ddr_data[24], wi_ddr_data[8] }, io_ddr_data[8]); |
|
xioddr p9(i_clk, ~o_ddr_we_n, { wo_ddr_data[25], wo_ddr_data[9] }, |
{ wi_ddr_data[25], wi_ddr_data[9] }, io_ddr_data[9]); |
|
xioddr pa(i_clk, ~o_ddr_we_n, { wo_ddr_data[26], wo_ddr_data[10] }, |
{ wi_ddr_data[26], wi_ddr_data[10] }, io_ddr_data[10]); |
|
xioddr pb(i_clk, ~o_ddr_we_n, { wo_ddr_data[27], wo_ddr_data[11] }, |
{ wi_ddr_data[27], wi_ddr_data[11] }, io_ddr_data[11]); |
|
xioddr pc(i_clk, ~o_ddr_we_n, { wo_ddr_data[28], wo_ddr_data[12] }, |
{ wi_ddr_data[28], wi_ddr_data[12] }, io_ddr_data[12]); |
|
xioddr pd(i_clk, ~o_ddr_we_n, { wo_ddr_data[29], wo_ddr_data[13] }, |
{ wi_ddr_data[29], wi_ddr_data[13] }, io_ddr_data[13]); |
|
xioddr pe(i_clk, ~o_ddr_we_n, { wo_ddr_data[30], wo_ddr_data[14] }, |
{ wi_ddr_data[30], wi_ddr_data[14] }, io_ddr_data[14]); |
|
xioddr pf(i_clk, ~o_ddr_we_n, { wo_ddr_data[31], wo_ddr_data[15] }, |
{ wi_ddr_data[31], wi_ddr_data[15] }, io_ddr_data[15]); |
|
OBUFTDS #(.IOSTANDARD("DIFF_SSTL135"), .SLEW("FAST")) |
dqsbuf0(.O(io_ddr_dqs_p[0]), .OB(io_ddr_dqs_n[0]), |
.I(w_ddr_dqs[1]), .T(w_ddr_dqs[2])); |
OBUFTDS #(.IOSTANDARD("DIFF_SSTL135"), .SLEW("FAST")) |
dqsbuf1(.O(io_ddr_dqs_p[1]), .OB(io_ddr_dqs_n[1]), |
.I(w_ddr_dqs[0]), .T(w_ddr_dqs[2])); |
|
OBUFDS #(.IOSTANDARD("DIFF_SSTL135"), .SLEW("FAST")) |
clkbuf(.O(o_ddr_ck_p), .OB(o_ddr_ck_n), .I(clk_for_ddr)); |
|
assign o_ddr_dm = 2'b00; |
assign o_ddr_odt = 1'b0; |
|
endmodule |
|
/rtl/wbucompactlines.v
0,0 → 1,117
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbucompactlines.v |
// |
// Project: FPGA library |
// |
// Purpose: Removes 'end of line' characters placed at the end of every |
// deworded word, unless we're idle or the line is too long. |
// This helps to format the output nicely to fit in an 80-character |
// display, should you need to do so for debugging. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
// When to apply a new line? |
// When no prior new line exists |
// or when prior line length exceeds (72) |
// Between codewords (need inserted newline) |
// When bus has become idle (~wb_cyc)&&(~busys) |
// |
// So, if every codeword ends in a newline, what we |
// really need to do is to remove newlines. Thus, if |
// i_stb goes high while i_tx_busy, we skip the newline |
// unless the line is empty. ... But i_stb will always |
// go high while i_tx_busy. How about if the line |
// length exceeds 72, we do nothing, but record the |
// last word. If the last word was a <incomplete-thought> |
// |
// |
module wbucompactlines(i_clk, i_stb, i_nl_hexbits, o_stb, o_nl_hexbits, |
i_bus_busy, i_tx_busy, o_busy); |
input i_clk, i_stb; |
input [6:0] i_nl_hexbits; |
output reg o_stb; |
output reg [6:0] o_nl_hexbits; |
input i_bus_busy; |
input i_tx_busy; |
output wire o_busy; |
|
reg last_out_nl, last_in_nl; |
initial last_out_nl = 1'b1; |
initial last_in_nl = 1'b1; |
always @(posedge i_clk) |
if ((~i_tx_busy)&&(o_stb)) |
last_out_nl <= (o_nl_hexbits[6]); |
always @(posedge i_clk) |
if ((i_stb)&&(~o_busy)) |
last_in_nl <= (i_nl_hexbits[6]); |
|
// Now, let's count how long our lines are |
reg [6:0] linelen; |
initial linelen = 7'h00; |
always @(posedge i_clk) |
if ((~i_tx_busy)&&(o_stb)) |
begin |
if (o_nl_hexbits[6]) |
linelen <= 0; |
else |
linelen <= linelen + 7'h1; |
end |
|
reg full_line; |
initial full_line = 1'b0; |
always @(posedge i_clk) |
full_line <= (linelen > 7'd72); |
|
|
// Now that we know whether or not the last character was a newline, |
// and indeed how many characters we have in any given line, we can |
// selectively remove newlines from our output stream. |
initial o_stb = 1'b0; |
always @(posedge i_clk) |
if ((i_stb)&&(~o_busy)) |
begin |
o_stb <= (full_line)||(~i_nl_hexbits[6]); |
o_nl_hexbits <= i_nl_hexbits; |
end else if (~o_busy) |
begin // Send an EOL if we are idle |
o_stb <= (~i_tx_busy)&&(~i_bus_busy)&&(~last_out_nl)&&(last_in_nl); |
o_nl_hexbits <= 7'h40; |
end else if (~i_tx_busy) |
o_stb <= 1'b0; |
|
reg r_busy; |
initial r_busy = 1'b0; |
always @(posedge i_clk) |
r_busy <= (o_stb); |
assign o_busy = (r_busy)||(o_stb); |
|
/* |
output wire [27:0] o_dbg; |
assign o_dbg = { o_stb, o_nl_hexbits, o_busy, r_busy, full_line, |
i_bus_busy, linelen, i_tx_busy, i_stb, i_nl_hexbits }; |
*/ |
endmodule |
/rtl/eqspiflash.v
0,0 → 1,1637
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: eqspiflash.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: Provide access to the flash device on an Arty, via the Extended |
// SPI interface. Reads and writes will use the QuadSPI interface |
// (4-bits at a time) all other commands (register and otherwise) will use |
// the SPI interface (1 bit at a time). |
// |
// Registers: |
// 0. Erase register control. Provides status of pending writes, erases, |
// and commands (sub)sector erase operations. |
// Bit-Fields: |
// 31. WIP (Write-In-Progress), write a '1' to this bit to command |
// an erase sequence. |
// 30. WriteEnabled -- set to a '1' to disable write protection and |
// to write a WRITE-ENABLE to the device. Set to a '0' to |
// disable WRITE-ENABLE-LATCH. (Key is required to enable |
// writes) |
// 29. Quad mode read/writes enabled. (Rest of controller will use |
// extended SPI mode, but reads and writes will use Quad |
// mode.) |
// 28. Subsector erase bit (set 1 to erase a subsector, 0 to |
// erase a full sector, maintains last value written from |
// an erase command, starts at '0') |
// 27. SD ID loaded |
// 26. Write protect violation--cleared upon any valid write |
// 25. XIP enabled. (Leave read mode in XIP, so you can start |
// next read faster.) |
// 24. Unused |
// 23..0: Address of erase sector upon erase command |
// 23..14: Sector address (can only be changed w/ key) |
// 23..10: Subsector address (can only be changed w/ key) |
// 9.. 0: write protect KEY bits, always read a '0', write |
// commands, such as WP disable or erase, must always |
// write with a '1be' to activate. |
// 0. WEL: All writes that do not command an erase will be used |
// to set/clear the write enable latch. |
// Send 0x06, return, if WP is clear (enable writes) |
// Send 0x04, return |
// 1. STATUS |
// Send 0x05, read 1-byte |
// Send 0x01, write 1-byte: i_wb_data[7:0] |
// 2. NV-CONFIG (16-bits) |
// Send 0xB5, read 2-bytes |
// Send 0xB1, write 2-bytes: i_wb_data[15:0] |
// 3. V-CONFIG (8-bits) |
// Send 0x85, read 1-byte |
// Send 0x81, write 1-byte: i_wb_data[7:0] |
// 4. EV-CONFIG (8-bits) |
// Send 0x65, read 1-byte |
// Send 0x61, write 1-byte: i_wb_data[7:0] |
// 5. Lock (send 32-bits, rx 1 byte) |
// Send 0xE8, last-sector-addr (3b), read 1-byte |
// Send 0xE5, last-sector-addr (3b), write 1-byte: i_wb_data[7:0] |
// 6. Flag Status |
// Send 0x70, read 1-byte |
// Send 0x50, to clear, no bytes to write |
// 7. Asynch Read-ID: Write here to cause controller to read ID into buffer |
// 8.-12. ID buffer (20 bytes, 5 words) |
// Attempted reads before buffer is full will stall bus until |
// buffer is read. Writes act like the asynch-Read-ID command, |
// and will cause the controller to read the buffer. |
// 13.-14. Unused, mapped to Asynch-read-ID |
// 15. OTP control word |
// Write zero to permanently lock OTP |
// Read to determine if OTP is permanently locked |
// 16.-31. OTP (64-bytes, 16 words, buffered until write) |
// (Send DWP before writing to clear write enable latch) |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
// `define QSPI_READ_ONLY |
module eqspiflash(i_clk_200mhz, i_rst, |
// Incoming wishbone connection(s) |
// The two strobe lines allow the data to live on a |
// separate part of the master bus from the control |
// registers. Only one strobe will ever be active at any |
// time, no strobes will ever be active unless i_wb_cyc |
// is also active. |
i_wb_cyc, i_wb_data_stb, i_wb_ctrl_stb, i_wb_we, |
i_wb_addr, i_wb_data, |
// Outgoing wishbone data |
o_wb_ack, o_wb_stall, o_wb_data, |
// Quad SPI connections |
o_qspi_sck, o_qspi_cs_n, o_qspi_mod, o_qspi_dat, i_qspi_dat, |
// Interrupt the CPU |
o_interrupt, o_cmd_accepted, |
// Debug the interface |
o_dbg); |
|
input i_clk_200mhz, i_rst; |
// Wishbone bus inputs |
input i_wb_cyc, i_wb_data_stb, i_wb_ctrl_stb, i_wb_we; |
input [21:0] i_wb_addr; // 24 bits of addr space |
input [31:0] i_wb_data; |
// Wishbone bus outputs |
output reg o_wb_ack; |
output wire o_wb_stall; |
output reg [31:0] o_wb_data; |
// Quad SPI connections |
output wire o_qspi_sck, o_qspi_cs_n; |
output wire [1:0] o_qspi_mod; |
output wire [3:0] o_qspi_dat; |
input wire [3:0] i_qspi_dat; |
// |
output reg o_interrupt; |
// |
output reg o_cmd_accepted; |
// |
output wire [31:0] o_dbg; |
|
initial o_cmd_accepted = 1'b0; |
always @(posedge i_clk_200mhz) |
o_cmd_accepted=((i_wb_data_stb)||(i_wb_ctrl_stb))&&(~o_wb_stall); |
// |
// lleqspi |
// |
// Providing the low-level SPI interface |
// |
reg spi_wr, spi_hold, spi_spd, spi_dir, spi_recycle; |
reg [31:0] spi_word; |
reg [1:0] spi_len; |
wire [31:0] spi_out; |
wire spi_valid, spi_busy, spi_stopped; |
lleqspi lowlvl(i_clk_200mhz, spi_wr, spi_hold, spi_word, spi_len, |
spi_spd, spi_dir, spi_recycle, spi_out, spi_valid, spi_busy, |
o_qspi_sck, o_qspi_cs_n, o_qspi_mod, o_qspi_dat, i_qspi_dat); |
assign spi_stopped = (o_qspi_cs_n)&&(~spi_busy)&&(~spi_wr); |
|
|
// |
// Bus module |
// |
// Providing a shared interface to the WB bus |
// |
// Wishbone data (returns) |
wire bus_wb_ack, bus_wb_stall; |
wire [31:0] bus_wb_data; |
// Latched request data |
wire bus_wr; |
wire [21:0] bus_addr; |
wire [31:0] bus_data; |
wire [21:0] bus_sector; |
// Strobe commands |
wire bus_ack; |
wire bus_readreq, bus_piperd, bus_ereq, bus_wreq, |
bus_pipewr, bus_endwr, bus_ctreq, bus_idreq, |
bus_other_req, |
// Live parameters |
w_xip, w_quad, w_idloaded; |
reg bus_wip; |
qspibus preproc(i_clk_200mhz, i_rst, |
i_wb_cyc, i_wb_data_stb, i_wb_ctrl_stb, |
i_wb_we, i_wb_addr, i_wb_data, |
bus_wb_ack, bus_wb_stall, bus_wb_data, |
bus_wr, bus_addr, bus_data, bus_sector, |
bus_readreq, bus_piperd, |
bus_wreq, bus_ereq, |
bus_pipewr, bus_endwr, |
bus_ctreq, bus_idreq, bus_other_req, bus_ack, |
w_xip, w_quad, w_idloaded, bus_wip, spi_stopped); |
|
// |
// Read flash module |
// |
// Providing a means of (and the logic to support) reading from |
// the flash |
// |
wire rd_data_ack; |
wire [31:0] rd_data; |
// |
wire rd_bus_ack; |
// |
wire rd_qspi_req; |
wire rd_qspi_grant; |
// |
wire rd_spi_wr, rd_spi_hold, rd_spi_spd, rd_spi_dir, |
rd_spi_recycle; |
wire [31:0] rd_spi_word; |
wire [1:0] rd_spi_len; |
// |
readqspi rdproc(i_clk_200mhz, bus_readreq, bus_piperd, |
bus_other_req, |
bus_addr, rd_bus_ack, |
rd_qspi_req, rd_qspi_grant, |
rd_spi_wr, rd_spi_hold, rd_spi_word, rd_spi_len, |
rd_spi_spd, rd_spi_dir, rd_spi_recycle, |
spi_out, spi_valid, |
spi_busy, spi_stopped, rd_data_ack, rd_data, |
w_quad, w_xip); |
|
// |
// Write/Erase flash module |
// |
// Logic to write (program) and erase the flash. |
// |
// Wishbone bus return |
wire ew_data_ack; |
wire [31:0] ew_data; |
// Arbiter interaction |
wire ew_qspi_req; |
wire ew_qspi_grant; |
// Bus controller return |
wire ew_bus_ack; |
// SPI control wires |
wire ew_spi_wr, ew_spi_hold, ew_spi_spd, ew_spi_dir; |
wire [31:0] ew_spi_word; |
wire [1:0] ew_spi_len; |
// |
wire w_ew_wip; |
// |
writeqspi ewproc(i_clk_200mhz, bus_wreq,bus_ereq, |
bus_pipewr, bus_endwr, |
bus_addr, bus_data, |
ew_bus_ack, ew_qspi_req, ew_qspi_grant, |
ew_spi_wr, ew_spi_hold, ew_spi_word, ew_spi_len, |
ew_spi_spd, ew_spi_dir, |
spi_out, spi_valid, spi_busy, spi_stopped, |
ew_data_ack, w_quad, w_ew_wip); |
|
// |
// Control module |
// |
// Logic to read/write status and configuration registers |
// |
// Wishbone bus return |
wire ct_data_ack; |
wire [31:0] ct_data; |
// Arbiter interaction |
wire ct_qspi_req; |
wire ct_grant; |
// Bus controller return |
wire ct_ack; |
// SPI control wires |
wire ct_spi_wr, ct_spi_hold, ct_spi_spd, ct_spi_dir; |
wire [31:0] ct_spi_word; |
wire [1:0] ct_spi_len; |
// |
ctrlspi ctproc(i_clk_200mhz, |
bus_ctreq, bus_wr, bus_addr[2:0], bus_data, bus_sector, |
ct_qspi_req, ct_grant, |
ct_spi_wr, ct_spi_hold, ct_spi_word, ct_spi_len, |
ct_spi_spd, ct_spi_dir, |
spi_out, spi_valid, spi_busy, spi_stopped, |
ct_ack, ct_data_ack, ct_data, w_xip, w_quad); |
assign ct_spi_hold = 1'b0; |
assign ct_spi_spd = 1'b0; |
|
// |
// ID/OTP module |
// |
// Access to ID and One-Time-Programmable registers, but to read |
// and to program (the OTP), and to finally lock (OTP) registers. |
// |
// Wishbone bus return |
wire id_data_ack; |
wire [31:0] id_data; |
// Arbiter interaction |
wire id_qspi_req; |
wire id_qspi_grant; |
// Bus controller return |
wire id_bus_ack; |
// SPI control wires |
wire id_spi_wr, id_spi_hold, id_spi_spd, id_spi_dir; |
wire [31:0] id_spi_word; |
wire [1:0] id_spi_len; |
// |
wire w_id_wip; |
// |
idotpqspi idotp(i_clk_200mhz, bus_idreq, |
bus_wr, bus_pipewr, bus_addr[4:0], bus_data, id_bus_ack, |
id_qspi_req, id_qspi_grant, |
id_spi_wr, id_spi_hold, id_spi_word, id_spi_len, |
id_spi_spd, id_spi_dir, |
spi_out, spi_valid, spi_busy, spi_stopped, |
id_data_ack, id_data, w_idloaded, w_id_wip); |
|
// Arbitrator |
reg owned; |
reg [1:0] owner; |
initial owned = 1'b0; |
always @(posedge i_clk_200mhz) // 7 inputs (spi_stopped is the CE) |
if ((~owned)&&(spi_stopped)) |
begin |
casez({rd_qspi_req,ew_qspi_req,id_qspi_req,ct_qspi_req}) |
4'b1???: begin owned<= 1'b1; owner <= 2'b00; end |
4'b01??: begin owned<= 1'b1; owner <= 2'b01; end |
4'b001?: begin owned<= 1'b1; owner <= 2'b10; end |
4'b0001: begin owned<= 1'b1; owner <= 2'b11; end |
default: begin owned<= 1'b0; owner <= 2'b00; end |
endcase |
end else if ((owned)&&(spi_stopped)) |
begin |
casez({rd_qspi_req,ew_qspi_req,id_qspi_req,ct_qspi_req,owner}) |
6'b0???00: owned<= 1'b0; |
6'b?0??01: owned<= 1'b0; |
6'b??0?10: owned<= 1'b0; |
6'b???011: owned<= 1'b0; |
default: begin ; end |
endcase |
end |
|
assign rd_qspi_grant = (owned)&&(owner == 2'b00); |
assign ew_qspi_grant = (owned)&&(owner == 2'b01); |
assign id_qspi_grant = (owned)&&(owner == 2'b10); |
assign ct_grant = (owned)&&(owner == 2'b11); |
|
// Module controller |
always @(posedge i_clk_200mhz) |
case(owner) |
2'b00: begin |
spi_wr <= (owned)&&(rd_spi_wr); |
spi_hold <= rd_spi_hold; |
spi_word <= rd_spi_word; |
spi_len <= rd_spi_len; |
spi_spd <= rd_spi_spd; |
spi_dir <= rd_spi_dir; |
spi_recycle <= rd_spi_recycle; |
end |
2'b01: begin |
spi_wr <= (owned)&&(ew_spi_wr); |
spi_hold <= ew_spi_hold; |
spi_word <= ew_spi_word; |
spi_len <= ew_spi_len; |
spi_spd <= ew_spi_spd; |
spi_dir <= ew_spi_dir; |
spi_recycle <= 1'b1; // Long recycle time |
end |
2'b10: begin |
spi_wr <= (owned)&&(id_spi_wr); |
spi_hold <= id_spi_hold; |
spi_word <= id_spi_word; |
spi_len <= id_spi_len; |
spi_spd <= id_spi_spd; |
spi_dir <= id_spi_dir; |
spi_recycle <= 1'b1; // Long recycle time |
end |
2'b11: begin |
spi_wr <= (owned)&&(ct_spi_wr); |
spi_hold <= ct_spi_hold; |
spi_word <= ct_spi_word; |
spi_len <= ct_spi_len; |
spi_spd <= ct_spi_spd; |
spi_dir <= ct_spi_dir; |
spi_recycle <= 1'b1; // Long recycle time |
end |
endcase |
|
reg last_wip; |
initial bus_wip = 1'b0; |
initial last_wip = 1'b0; |
initial o_interrupt = 1'b0; |
always @(posedge i_clk_200mhz) |
begin |
bus_wip <= w_ew_wip || w_id_wip; |
last_wip <= bus_wip; |
o_interrupt <= ((~bus_wip)&&(last_wip)); |
end |
|
|
// Now, let's return values onto the wb bus |
always @(posedge i_clk_200mhz) |
begin |
// Ack our internal bus controller. This means the command was |
// accepted, and the bus can go on to looking for the next |
// command. It controls the i_wb_stall line, just not the |
// i_wb_ack line. |
|
// Ack the wishbone with any response |
o_wb_ack <= (bus_wb_ack)|(rd_data_ack)|(ew_data_ack)|(id_data_ack)|(ct_data_ack); |
o_wb_data <= (bus_wb_ack)?bus_wb_data |
: (id_data_ack) ? id_data : spi_out; |
end |
|
assign o_wb_stall = bus_wb_stall; |
assign bus_ack = (rd_bus_ack|ew_bus_ack|id_bus_ack|ct_ack); |
|
assign o_dbg = { |
i_wb_cyc, i_wb_ctrl_stb, i_wb_data_stb, o_wb_ack, bus_ack, //5 |
// |
(spi_wr)&&(~spi_busy), spi_valid, spi_word[31:25], |
spi_out[7:2], |
// |
o_qspi_cs_n, o_qspi_sck, o_qspi_mod, // 4 bits |
o_qspi_dat, i_qspi_dat // 8 bits |
}; |
endmodule |
|
module qspibus(i_clk, i_rst, i_cyc, i_data_stb, i_ctrl_stb, |
i_we, i_addr, i_data, |
o_wb_ack, o_wb_stall, o_wb_data, |
o_wr, o_addr, o_data, o_sector, |
o_readreq, o_piperd, o_wrreq, o_erreq, o_pipewr, o_endwr, |
o_ctreq, o_idreq, o_other, |
i_ack, i_xip, i_quad, i_idloaded, i_wip, i_spi_stopped); |
// |
input i_clk, i_rst; |
// Wishbone bus inputs |
input i_cyc, i_data_stb, i_ctrl_stb, i_we; |
input [21:0] i_addr; |
input [31:0] i_data; |
// Wishbone bus outputs |
output reg o_wb_ack; |
output reg o_wb_stall; |
output wire [31:0] o_wb_data; |
// Internal signals to the QSPI flash interface |
output reg o_wr; |
output reg [21:0] o_addr; |
output reg [31:0] o_data; |
output wire [21:0] o_sector; |
output reg o_readreq, o_piperd, o_wrreq, o_erreq, |
o_pipewr, o_endwr, |
o_ctreq, o_idreq; |
output wire o_other; |
input i_ack, i_xip, i_quad, i_idloaded; |
input i_wip, i_spi_stopped; |
|
|
// |
reg pending, lcl_wrreq, lcl_ctreq, lcl_ack, ack, wp_err, wp; |
reg lcl_reg; |
reg [14:0] esector; |
reg [21:0] next_addr; |
|
|
reg pipeable; |
reg same_page; |
always @(posedge i_clk) |
same_page <= (i_data_stb)&&(i_we) |
&&(i_addr[21:6] == o_addr[21:6]) |
&&(i_addr[5:0] == o_addr[5:0] + 6'h1); |
|
initial pending = 1'b0; |
initial o_readreq = 1'b0; |
initial lcl_wrreq = 1'b0; |
initial lcl_ctreq = 1'b0; |
initial o_ctreq = 1'b0; |
initial o_idreq = 1'b0; |
|
initial ack = 1'b0; |
always @(posedge i_clk) |
ack <= (i_ack)||(lcl_ack); |
|
// wire [9:0] key; |
// assign key = 10'h1be; |
reg lcl_key, set_sector, ctreg_stb; |
initial lcl_key = 1'b0; |
always @(posedge i_clk) |
// Write protect "key" to enable the disabling of write protect |
lcl_key<= (i_ctrl_stb)&&(~wp)&&(i_we)&&(i_addr[5:0]==6'h00) |
&&(i_data[9:0] == 10'h1be)&&(i_data[31:30]==2'b11); |
initial set_sector = 1'b0; |
always @(posedge i_clk) |
set_sector <= (i_ctrl_stb)&&(~o_wb_stall) |
&&(i_we)&&(i_addr[5:0]==6'h00) |
&&(i_data[9:0] == 10'h1be); |
|
always @(posedge i_clk) |
if (i_ctrl_stb) |
lcl_reg <= (i_addr[3:0] == 4'h00); |
|
initial ctreg_stb = 1'b0; |
initial o_wb_stall = 1'b0; |
always @(posedge i_clk) |
begin // Inputs: rst, stb, stb, stall, ack, addr[4:0] -- 9 |
if (i_rst) |
o_wb_stall <= 1'b0; |
else |
o_wb_stall <= (((i_data_stb)||(i_ctrl_stb))&&(~o_wb_stall)) |
||((pending)&&(~ack)); |
|
ctreg_stb <= (i_ctrl_stb)&&(~o_wb_stall)&&(i_addr[4:0]==5'h00)&&(~pending) |
||(pending)&&(ctreg_stb)&&(~lcl_ack)&&(~i_ack); |
if (~o_wb_stall) |
begin // Bus command accepted! |
if ((i_data_stb)||(i_ctrl_stb)) |
begin |
pending <= 1'b1; |
o_addr <= i_addr; |
o_data <= i_data; |
o_wr <= i_we; |
next_addr <= i_addr + 22'h1; |
end |
|
if ((i_data_stb)&&(~i_we)) |
o_readreq <= 1'b1; |
|
if ((i_data_stb)&&(i_we)) |
lcl_wrreq <= 1'b1; |
if ((i_ctrl_stb)&&(~i_addr[4])) |
begin |
casez(i_addr[4:0]) |
5'h0: lcl_ctreq<= 1'b1; |
5'h1: lcl_ctreq <= 1'b1; |
5'h2: lcl_ctreq <= 1'b1; |
5'h3: lcl_ctreq <= 1'b1; |
5'h4: lcl_ctreq <= 1'b1; |
5'h5: lcl_ctreq <= 1'b1; |
5'h6: lcl_ctreq <= 1'b1; |
5'h7: lcl_ctreq <= 1'b1; |
5'h8: o_idreq <= 1'b1; // ID[0] |
5'h9: o_idreq <= 1'b1; // ID[1] |
5'ha: o_idreq <= 1'b1; // ID[2] |
5'hb: o_idreq <= 1'b1; // ID[3] |
5'hc: o_idreq <= 1'b1; // ID[4] |
5'hd: o_idreq <= 1'b1; // |
5'he: o_idreq <= 1'b1; |
5'hf: o_idreq <= 1'b1; // Program OTP register |
default: begin o_idreq <= 1'b1; end |
endcase |
end else if (i_ctrl_stb) |
o_idreq <= 1'b1; |
end else if (ack) |
begin |
pending <= 1'b0; |
o_readreq <= 1'b0; |
o_idreq <= 1'b0; |
lcl_ctreq <= 1'b0; |
lcl_wrreq <= 1'b0; |
end |
|
if(i_rst) |
begin |
pending <= 1'b0; |
o_readreq <= 1'b0; |
o_idreq <= 1'b0; |
lcl_ctreq <= 1'b0; |
lcl_wrreq <= 1'b0; |
end |
|
if ((i_data_stb)&&(~o_wb_stall)) |
o_piperd <= ((~i_we)&&(~o_wb_stall)&&(pipeable)&&(i_addr == next_addr)); |
else if ((i_ack)||(((i_ctrl_stb)||(i_data_stb))&&(~o_wb_stall))) |
o_piperd <= 1'b0; |
if ((i_data_stb)&&(~o_wb_stall)) |
pipeable <= (~i_we); |
else if ((i_ctrl_stb)&&(~o_wb_stall)) |
pipeable <= 1'b0; |
|
o_pipewr <= (same_page)||(pending)&&(o_pipewr); |
end |
|
reg r_other, last_wip; |
|
reg last_pending; |
always @(posedge i_clk) |
last_pending <= pending; |
always @(posedge i_clk) |
last_wip <= i_wip; |
wire new_req; |
assign new_req = (pending)&&(~last_pending); |
|
initial o_wrreq = 1'b0; |
initial o_erreq = 1'b0; |
initial wp_err = 1'b0; |
initial lcl_ack = 1'b0; |
initial r_other = 1'b0; |
initial o_endwr = 1'b1; |
initial wp = 1'b1; |
always @(posedge i_clk) |
begin |
if (i_ack) |
begin |
o_erreq <= 1'b0; |
o_wrreq <= 1'b0; |
o_ctreq <= 1'b0; |
r_other <= 1'b0; |
end |
|
if ((last_wip)&&(~i_wip)) |
wp <= 1'b1; |
|
// o_endwr <= ((~i_cyc)||(~o_wr)||(o_pipewr)) |
// ||(~new_req)&&(o_endwr); |
o_endwr <= ((pending)&&(~o_pipewr))||((~pending)&&(~i_cyc)); |
|
// Default ACK is always set to zero, unless the following ... |
o_wb_ack <= 1'b0; |
|
if (set_sector) |
begin |
esector[13:0] <= { o_data[23:14], 4'h0 }; |
wp <= (o_data[30])&&(new_req)||(wp)&&(~new_req); |
if (o_data[28]) |
begin |
esector[14] <= o_data[28]; |
esector[3:0] <= o_data[13:10]; |
end |
end |
|
lcl_ack <= 1'b0; |
if ((i_wip)&&(new_req)&&(~same_page)) |
begin |
o_wb_ack <= 1'b1; |
lcl_ack <= 1'b1; |
end else if ((ctreg_stb)&&(new_req)) |
begin // A request of the status register |
// Always ack control register, even on failed attempts |
// to erase. |
o_wb_ack <= 1'b1; |
lcl_ack <= 1'b1; |
|
if (lcl_key) |
begin |
o_ctreq <= 1'b0; |
o_erreq <= 1'b1; |
r_other <= 1'b1; |
lcl_ack <= 1'b0; |
end else if ((o_wr)&&(~o_data[31])) |
begin // WEL or WEL disable |
o_ctreq <= (wp == o_data[30]); |
r_other <= (wp == o_data[30]); |
lcl_ack <= (wp != o_data[30]); |
wp <= !o_data[30]; |
end else if (~o_wr) |
lcl_ack <= 1'b1; |
wp_err <= (o_data[31])&&(~lcl_key); |
end else if ((lcl_ctreq)&&(new_req)) |
begin |
o_ctreq <= 1'b1; |
r_other <= 1'b1; |
end else if ((lcl_wrreq)&&(new_req)) |
begin |
if (~wp) |
begin |
o_wrreq <= 1'b1; |
r_other <= 1'b1; |
o_endwr <= 1'b0; |
lcl_ack <= 1'b0; |
end else begin |
o_wb_ack <= 1'b1; |
wp_err <= 1'b1; |
lcl_ack <= 1'b1; |
end |
end |
|
if (i_rst) |
begin |
o_ctreq <= 1'b0; |
o_erreq <= 1'b0; |
o_wrreq <= 1'b0; |
r_other <= 1'b0; |
end |
|
end |
|
|
assign o_wb_data[31:0] = { i_wip, ~wp, i_quad, esector[14], |
i_idloaded, wp_err, i_xip, i_spi_stopped, |
esector[13:0], 10'h00 }; |
assign o_sector = { esector[13:0], 8'h00 }; // 22 bits |
assign o_other = (r_other)||(o_idreq); |
|
endmodule |
|
|
`define RD_IDLE 4'h0 |
`define RD_IDLE_GET_PORT 4'h1 |
`define RD_SLOW_DUMMY 4'h2 |
`define RD_SLOW_READ_DATA 4'h3 |
`define RD_QUAD_READ_DATA 4'h4 |
`define RD_QUAD_DUMMY 4'h5 |
`define RD_QUAD_ADDRESS 4'h6 |
`define RD_XIP 4'h7 |
`define RD_GO_TO_IDLE 4'h8 |
`define RD_GO_TO_XIP 4'h9 |
|
module readqspi(i_clk, i_readreq, i_piperd, i_other_req, i_addr, o_bus_ack, |
o_qspi_req, i_grant, |
o_spi_wr, o_spi_hold, o_spi_word, o_spi_len, |
o_spi_spd, o_spi_dir, o_spi_recycle, |
i_spi_data, i_spi_valid, i_spi_busy, i_spi_stopped, |
o_data_ack, o_data, i_quad, i_xip); |
input i_clk; |
input i_readreq, i_piperd, i_other_req; |
input [21:0] i_addr; |
output reg o_bus_ack, o_qspi_req; |
input wire i_grant; |
output reg o_spi_wr; |
output wire o_spi_hold; |
output reg [31:0] o_spi_word; |
output reg [1:0] o_spi_len; |
output reg o_spi_spd, o_spi_dir, o_spi_recycle; |
input [31:0] i_spi_data; |
input i_spi_valid, i_spi_busy, i_spi_stopped; |
output reg o_data_ack; |
output reg [31:0] o_data; |
input i_quad, i_xip; |
|
reg accepted; |
initial accepted = 1'b0; |
always @(posedge i_clk) |
accepted <= (~i_spi_busy)&&(i_grant)&&(o_spi_wr)&&(~accepted); |
|
reg [3:0] rd_state; |
reg r_leave_xip, r_xip, r_quad, r_requested; |
initial rd_state = `RD_IDLE; |
initial o_data_ack = 1'b0; |
initial o_bus_ack = 1'b0; |
initial o_qspi_req = 1'b0; |
always @(posedge i_clk) |
begin |
o_data_ack <= 1'b0; |
o_bus_ack <= 1'b0; |
o_spi_recycle <= 1'b0; |
if (i_spi_valid) |
o_data <= i_spi_data; |
case(rd_state) |
`RD_IDLE: begin |
r_requested <= 1'b0; |
o_qspi_req <= 1'b0; |
o_spi_word <= { ((i_quad)? 8'h6B: 8'h0b), i_addr, 2'b00 }; |
o_spi_wr <= 1'b0; |
o_spi_dir <= 1'b0; |
o_spi_spd <= 1'b0; |
o_spi_len <= 2'b11; |
r_xip <= (i_xip)&&(i_quad); |
r_leave_xip <= 1'b0; // Not in it, so can't leave it |
r_quad <= i_quad; |
if (i_readreq) |
begin |
rd_state <= `RD_IDLE_GET_PORT; |
o_bus_ack <= 1'b1; |
end end |
`RD_IDLE_GET_PORT: begin |
o_spi_wr <= 1'b1; // Write the address |
o_qspi_req <= 1'b1; |
if (accepted) |
rd_state <= `RD_SLOW_DUMMY; |
end |
`RD_SLOW_DUMMY: begin |
o_spi_wr <= 1'b1; // Write 8 dummy clocks |
o_qspi_req <= 1'b1; |
o_spi_dir <= 1'b0; |
o_spi_spd <= 1'b0; |
o_spi_word[31:24] <= (r_xip) ? 8'h00 : 8'hff; |
o_spi_len <= 2'b00; // 8 clocks = 8-bits |
if (accepted) |
rd_state <= (r_quad)?`RD_QUAD_READ_DATA |
: `RD_SLOW_READ_DATA; |
end |
`RD_SLOW_READ_DATA: begin |
o_qspi_req <= 1'b1; |
o_spi_dir <= 1'b1; |
o_spi_spd <= 1'b0; |
o_spi_len <= 2'b11; |
o_spi_wr <= (~r_requested)||(i_piperd); |
// if (accepted) |
// o_spi_wr <= (i_piperd); |
o_data_ack <= (r_requested)&&(i_spi_valid); |
o_bus_ack <= (r_requested)&&(accepted)&&(i_piperd); |
r_requested <= (r_requested)||(accepted); |
if ((i_spi_valid)&&(~o_spi_wr)) |
rd_state <= `RD_GO_TO_IDLE; |
end |
`RD_QUAD_READ_DATA: begin |
o_qspi_req <= 1'b1; |
o_spi_dir <= 1'b1; |
o_spi_spd <= 1'b1; |
o_spi_len <= 2'b11; |
o_spi_recycle <= (r_leave_xip)? 1'b1: 1'b0; |
r_requested <= (r_requested)||(accepted); |
o_data_ack <= (r_requested)&&(i_spi_valid)&&(~r_leave_xip); |
o_bus_ack <= (r_requested)&&(accepted)&&(i_piperd)&&(~r_leave_xip); |
o_spi_wr <= (~r_requested)||(i_piperd); |
// if (accepted) |
// o_spi_wr <= (i_piperd); |
if (accepted) |
o_data <= i_spi_data; |
if ((i_spi_valid)&&(~o_spi_wr)) |
rd_state <= ((r_leave_xip)||(~r_xip))?`RD_GO_TO_IDLE:`RD_GO_TO_XIP; |
end |
`RD_QUAD_ADDRESS: begin |
o_qspi_req <= 1'b1; |
o_spi_wr <= 1'b1; |
o_spi_dir <= 1'b0; // Write the address |
o_spi_spd <= 1'b1; // High speed |
o_spi_word[31:0] <= { i_addr, 2'b00, 8'h00 }; |
o_spi_len <= 2'b10; // 24 bits, High speed, 6 clocks |
if (accepted) |
rd_state <= `RD_QUAD_DUMMY; |
end |
`RD_QUAD_DUMMY: begin |
o_qspi_req <= 1'b1; |
o_spi_wr <= 1'b1; |
o_spi_dir <= 1'b0; // Write the dummy |
o_spi_spd <= 1'b1; // High speed |
o_spi_word[31:0] <= (r_xip)? 32'h00 : 32'hffffffff; |
o_spi_len <= 2'b11; // 8 clocks = 32-bits, quad speed |
if (accepted) |
rd_state <= (r_quad)?`RD_QUAD_READ_DATA |
: `RD_SLOW_READ_DATA; |
end |
`RD_XIP: begin |
r_requested <= 1'b0; |
o_qspi_req <= 1'b1; |
o_spi_word <= { i_addr, 2'b00, 8'h00 }; |
o_spi_wr <= 1'b0; |
o_spi_dir <= 1'b0; // Write to SPI |
o_spi_spd <= 1'b1; // High speed |
o_spi_len <= 2'b11; |
r_leave_xip <= i_other_req; |
r_xip <= (~i_other_req); |
o_bus_ack <= 1'b0; |
if ((i_readreq)||(i_other_req)) |
begin |
rd_state <= `RD_QUAD_ADDRESS; |
o_bus_ack <= i_readreq; |
end end |
`RD_GO_TO_IDLE: begin |
o_qspi_req <= 1'b0; |
o_spi_wr <= 1'b0; |
if ((i_spi_stopped)&&(~i_grant)) |
rd_state <= `RD_IDLE; |
end |
`RD_GO_TO_XIP: begin |
o_qspi_req <= 1'b1; |
o_spi_wr <= 1'b0; |
if (i_spi_stopped) |
rd_state <= `RD_XIP; |
end |
default: begin |
// rd_state <= (i_grant)?`RD_BREAK; |
o_qspi_req <= 1'b0; |
o_spi_wr <= 1'b0; |
if ((i_spi_stopped)&&(~i_grant)) |
rd_state <= `RD_IDLE; |
end |
endcase |
end |
|
assign o_spi_hold = 1'b0; |
|
endmodule |
|
module writeqspi(i_clk, i_wreq, i_ereq, i_pipewr, i_endpipe, i_addr, i_data, |
o_bus_ack, o_qspi_req, i_qspi_grant, |
o_spi_wr, o_spi_hold, o_spi_word, o_spi_len, |
o_spi_spd, o_spi_dir, i_spi_data, i_spi_valid, |
i_spi_busy, i_spi_stopped, |
o_data_ack, i_quad, o_wip); |
input i_clk; |
// |
input i_wreq, i_ereq, i_pipewr, i_endpipe; |
input [21:0] i_addr; |
input [31:0] i_data; |
output reg o_bus_ack, o_qspi_req; |
input i_qspi_grant; |
output reg o_spi_wr, o_spi_hold; |
output reg [31:0] o_spi_word; |
output reg [1:0] o_spi_len; |
output reg o_spi_spd, o_spi_dir; |
input [31:0] i_spi_data; |
input i_spi_valid; |
input i_spi_busy, i_spi_stopped; |
output reg o_data_ack; |
input i_quad; |
output reg o_wip; |
|
`ifdef QSPI_READ_ONLY |
always @(posedge i_clk) |
o_data_ack <= (i_wreq)||(i_ereq); |
always @(posedge i_clk) |
o_bus_ack <= (i_wreq)||(i_ereq); |
|
always @(posedge i_clk) |
begin |
o_qspi_req <= 1'b0; |
o_spi_wr <= 1'b0; |
o_spi_hold <= 1'b0; |
o_spi_dir <= 1'b1; // Read |
o_spi_spd <= i_quad; |
o_spi_len <= 2'b00; |
o_spi_word <= 32'h00; |
o_wip <= 1'b0; |
end |
`else |
|
`define WR_IDLE 4'h0 |
`define WR_START_WRITE 4'h1 |
`define WR_START_QWRITE 4'h2 |
`define WR_PROGRAM 4'h3 |
`define WR_PROGRAM_GETNEXT 4'h4 |
`define WR_START_ERASE 4'h5 |
`define WR_WAIT_ON_STOP 4'h6 |
`define WR_REQUEST_STATUS 4'h7 |
`define WR_REQUEST_STATUS_NEXT 4'h8 |
`define WR_READ_STATUS 4'h9 |
`define WR_WAIT_ON_FINAL_STOP 4'ha |
|
reg accepted; |
initial accepted = 1'b0; |
always @(posedge i_clk) |
accepted <= (~i_spi_busy)&&(i_qspi_grant)&&(o_spi_wr)&&(~accepted); |
|
|
reg cyc, chk_wip; |
reg [3:0] wr_state; |
initial wr_state = `WR_IDLE; |
initial cyc = 1'b0; |
always @(posedge i_clk) |
begin |
chk_wip <= 1'b0; |
o_bus_ack <= 1'b0; |
o_data_ack <= 1'b0; |
cyc <= (cyc)&&(~i_endpipe); |
case(wr_state) |
`WR_IDLE: begin |
o_qspi_req <= 1'b0; |
cyc <= 1'b0; |
if (i_ereq) |
wr_state <= `WR_START_ERASE; |
else if (i_wreq) |
wr_state <= (i_quad)?`WR_START_QWRITE |
: `WR_START_WRITE; |
end |
`WR_START_WRITE: begin |
o_wip <= 1'b1; |
o_qspi_req <= 1'b1; |
o_spi_wr <= 1'b1; |
o_spi_dir <= 1'b0; |
o_spi_len <= 2'b11; |
o_spi_spd <= 1'b0; |
o_spi_hold <= 1'b1; |
o_spi_word <= { 8'h02, i_addr, 2'b00 }; |
cyc <= 1'b1; |
if (accepted) |
begin |
o_bus_ack <= 1'b1; |
o_data_ack <= 1'b1; |
wr_state <= `WR_PROGRAM; |
o_spi_word <= i_data; |
end end |
`WR_START_QWRITE: begin |
o_wip <= 1'b1; |
o_qspi_req <= 1'b1; |
o_spi_wr <= 1'b1; |
o_spi_dir <= 1'b0; |
o_spi_len <= 2'b11; |
o_spi_spd <= 1'b0; |
o_spi_hold <= 1'b1; |
o_spi_word <= { 8'h32, i_addr, 2'b00 }; |
cyc <= 1'b1; |
if (accepted) |
begin |
o_bus_ack <= 1'b1; |
o_data_ack <= 1'b1; |
wr_state <= `WR_PROGRAM; |
o_spi_word <= i_data; |
end end |
`WR_PROGRAM: begin |
o_wip <= 1'b1; |
o_qspi_req <= 1'b1; |
o_spi_wr <= 1'b1; |
o_spi_dir <= 1'b0; |
o_spi_len <= 2'b11; |
o_spi_spd <= i_quad; |
o_spi_hold <= 1'b1; |
// o_spi_word <= i_data; |
if (accepted) |
wr_state <= `WR_PROGRAM_GETNEXT; |
end |
`WR_PROGRAM_GETNEXT: begin |
o_wip <= 1'b1; |
o_qspi_req <= 1'b1; |
o_spi_wr <= 1'b0; |
o_spi_dir <= 1'b0; |
o_spi_len <= 2'b11; |
o_spi_spd <= i_quad; |
o_spi_hold <= 1'b1; |
o_spi_word <= i_data; |
if (~cyc) |
wr_state <= `WR_WAIT_ON_STOP; |
else if (i_pipewr) |
begin |
o_bus_ack <= 1'b1; |
o_data_ack <= 1'b1; |
wr_state <= `WR_PROGRAM; |
end end |
`WR_START_ERASE: begin |
o_wip <= 1'b1; |
o_qspi_req <= 1'b1; |
o_spi_wr <= 1'b1; |
o_spi_dir <= 1'b0; |
o_spi_spd <= 1'b0; |
o_spi_len <= 2'b11; |
if (i_data[28]) |
// Subsector erase |
o_spi_word[31:24] <= 8'h20; |
else |
// Sector erase |
o_spi_word[31:24] <= 8'hd8; |
o_spi_word[23:0] <= { i_data[21:10], 12'h0 }; |
// Data has already been ack'd, so no need to ack |
// it again. However, we can now free the QSPI |
// bus processor to accept another command from the |
// bus. |
o_bus_ack <= accepted; |
if (accepted) |
wr_state <= `WR_WAIT_ON_STOP; |
end |
`WR_WAIT_ON_STOP: begin |
o_wip <= 1'b1; |
o_qspi_req <= 1'b0; |
o_spi_wr <= 1'b0; |
o_spi_hold <= 1'b0; |
if (i_spi_stopped) |
wr_state <= `WR_REQUEST_STATUS; |
end |
`WR_REQUEST_STATUS: begin |
o_wip <= 1'b1; |
o_qspi_req <= 1'b1; |
o_spi_hold <= 1'b0; |
o_spi_wr <= 1'b1; |
o_spi_spd <= 1'b0; // Slow speed |
o_spi_len <= 2'b00; // 8 bytes |
o_spi_dir <= 1'b0; // Write |
o_spi_word <= { 8'h05, 24'h00 }; |
if (accepted) |
wr_state <= `WR_REQUEST_STATUS_NEXT; |
end |
`WR_REQUEST_STATUS_NEXT: begin |
o_wip <= 1'b1; |
o_qspi_req <= 1'b1; |
o_spi_hold <= 1'b0; |
o_spi_wr <= 1'b1; |
o_spi_spd <= 1'b0; // Slow speed |
o_spi_len <= 2'b00; // 8 bytes |
o_spi_dir <= 1'b1; // Read |
o_spi_word <= 32'h00; |
if (accepted) |
wr_state <= `WR_READ_STATUS; |
end |
`WR_READ_STATUS: begin |
o_wip <= 1'b1; |
o_qspi_req <= 1'b1; |
o_spi_hold <= 1'b0; |
o_spi_wr <= 1'b1; |
o_spi_spd <= 1'b0; // Slow speed |
o_spi_len <= 2'b00; // 8 bytes |
o_spi_dir <= 1'b1; // Read |
o_spi_word <= 32'h00; |
if (i_spi_valid) |
chk_wip <= 1'b1; |
if ((chk_wip)&&(~i_spi_data[0])) |
wr_state <= `WR_WAIT_ON_FINAL_STOP; |
end |
// `WR_WAIT_ON_FINAL_STOP: // Same as the default |
default: begin |
o_qspi_req <= 1'b0; |
o_spi_wr <= 1'b0; |
o_wip <= 1'b0; |
if (i_spi_stopped) |
wr_state <= `WR_IDLE; |
end |
endcase |
end |
`endif |
|
endmodule |
|
|
`define CT_SAFE |
`define CT_IDLE 3'h0 |
`define CT_NEXT 3'h1 |
`define CT_GRANTED 3'h2 |
`define CT_DATA 3'h3 |
`define CT_READ_DATA 3'h4 |
`define CT_WAIT_FOR_IDLE 3'h5 |
|
// CTRL commands: |
// WEL (write-enable latch) |
// Read Status |
module ctrlspi(i_clk, i_req, i_wr, i_addr, i_data, i_sector_address, |
o_spi_req, i_grant, |
o_spi_wr, o_spi_hold, o_spi_word, o_spi_len, |
o_spi_spd, o_spi_dir, |
i_spi_data, i_spi_valid, i_spi_busy, |
i_spi_stopped, |
o_bus_ack, o_data_ack, o_data, o_xip, o_quad); |
input i_clk; |
// From the WB bus controller |
input i_req; |
input i_wr; |
input [2:0] i_addr; |
input [31:0] i_data; |
input [21:0] i_sector_address; |
// To/from the arbiter |
output reg o_spi_req; |
input i_grant; |
// To/from the low-level SPI driver |
output reg o_spi_wr; |
output wire o_spi_hold; |
output reg [31:0] o_spi_word; |
output reg [1:0] o_spi_len; |
output wire o_spi_spd; |
output reg o_spi_dir; |
input [31:0] i_spi_data; |
input i_spi_valid; |
input i_spi_busy, i_spi_stopped; |
// Return data to the bus controller, and the wishbone bus |
output reg o_bus_ack, o_data_ack; |
output reg [31:0] o_data; |
// Configuration items that we may have configured. |
output reg o_xip; |
output wire o_quad; |
|
// Command registers |
reg [1:0] ctcmd_len; |
reg [31:0] ctcmd_word; |
// Data stage registers |
reg ctdat_skip, // Skip the data phase? |
ctdat_wr; // Write during data? (or not read) |
wire [1:0] ctdat_len; |
reg [31:0] ctdat_word; |
|
reg [2:0] ctstate; |
reg accepted; |
|
|
initial accepted = 1'b0; |
always @(posedge i_clk) |
accepted <= (~i_spi_busy)&&(i_grant)&&(o_spi_wr)&&(~accepted); |
|
reg r_ctdat_len, ctbus_ack; |
assign ctdat_len = { 1'b0, r_ctdat_len }; |
|
// First step, calculate the values for our state machine |
initial o_xip = 1'b0; |
// initial o_quad = 1'b0; |
always @(posedge i_clk) |
if (i_req) // A request for us to act from the bus controller |
begin |
ctdat_skip <= 1'b0; |
ctbus_ack <= 1'b1; |
ctcmd_word[23:0] <= { i_sector_address, 2'b00 }; |
ctdat_word <= { i_data[7:0], 24'h00 }; |
ctcmd_len <= 2'b00; // 8bit command (for all but Lock regs) |
r_ctdat_len <= 1'b0; // 8bit data (read or write) |
ctdat_wr <= i_wr; |
casez({ i_addr[2:0], i_wr, i_data[30] }) |
5'b00010: begin // Write Disable |
ctcmd_word[31:24] <= 8'h04; |
ctdat_skip <= 1'b1; |
ctbus_ack <= 1'b0; |
end |
5'b00011: begin // Write enable |
ctcmd_word[31:24] <= 8'h06; |
ctdat_skip <= 1'b1; |
ctbus_ack <= 1'b0; |
end |
// 4'b0010?: begin // Read Status register |
// Moved to defaults section |
5'b0011?: begin // Write Status register (Requires WEL) |
ctcmd_word[31:24] <= 8'h01; |
`ifdef CT_SAFE |
ctdat_word <= { 6'h00, i_data[1:0], 24'h00 }; |
`else |
ctdat_word <= { i_data[7:0], 24'h00 }; |
`endif |
end |
5'b0100?: begin // Read NV-Config register (two bytes) |
ctcmd_word[31:24] <= 8'hB5; |
r_ctdat_len <= 1'b1; // 16-bit data |
end |
5'b0101?: begin // Write NV-Config reg (2 bytes, Requires WEL) |
ctcmd_word[31:24] <= 8'hB1; |
r_ctdat_len <= 1'b1; // 16-bit data |
`ifdef CT_SAFE |
ctdat_word <= { 4'h8, 3'h7, 3'h7, i_data[5:1], 1'b1, 16'h00 }; |
`else |
ctdat_word <= { i_data[15:0], 16'h00 }; |
`endif |
end |
5'b0110?: begin // Read V-Config register |
ctcmd_word[31:24] <= 8'h85; |
end |
5'b0111?: begin // Write V-Config register (Requires WEL) |
ctcmd_word[31:24] <= 8'h81; |
r_ctdat_len <= 1'b0; // 8-bit data |
`ifdef CT_SAFE |
ctdat_word <= { 4'h8, i_data[3:2], 2'b11, 24'h00 }; |
`else |
ctdat_word <= { i_data[7:0], 24'h00 }; |
`endif |
o_xip <= i_data[3]; |
end |
5'b1000?: begin // Read EV-Config register |
ctcmd_word[31:24] <= 8'h65; |
end |
5'b1001?: begin // Write EV-Config register (Requires WEL) |
ctcmd_word[31:24] <= 8'h61; |
// o_quad <= (~i_data[7]); |
`ifdef CT_SAFE |
ctdat_word <= { 1'b1, 3'h5, 4'hf, 24'h00 }; |
`else |
ctdat_word <= { i_data[7:0], 24'h00 }; |
`endif |
end |
5'b1010?: begin // Read Lock register |
ctcmd_word[31:0] <= { 8'he8, i_sector_address, 2'b00 }; |
ctcmd_len <= 2'b11; |
ctdat_wr <= 1'b0; // Read, not write |
end |
5'b1011?: begin // Write Lock register (Requires WEL) |
ctcmd_word[31:0] <= { 8'he5, i_sector_address, 2'b00 }; |
ctcmd_len <= 2'b11; |
ctdat_wr <= 1'b1; // Write |
end |
5'b1100?: begin // Read Flag Status register |
ctcmd_word[31:24] <= 8'h70; |
ctdat_wr <= 1'b0; // Read, not write |
end |
5'b1101?: begin // Write/Clear Flag Status register (No WEL required) |
ctcmd_word[31:24] <= 8'h50; |
ctdat_skip <= 1'b1; |
end |
default: begin // Default to reading the status register |
ctcmd_word[31:24] <= 8'h05; |
ctdat_wr <= 1'b0; // Read, not write |
r_ctdat_len <= 1'b0; // 8-bit data |
end |
endcase |
end |
|
assign o_quad = 1'b1; |
|
reg nxt_data_ack; |
|
// Second step, actually drive the state machine |
initial ctstate = `CT_IDLE; |
always @(posedge i_clk) |
begin |
o_spi_wr <= 1'b1; |
o_bus_ack <= 1'b0; |
o_data_ack <= 1'b0; |
if (i_spi_valid) |
o_data <= i_spi_data; |
case(ctstate) |
`CT_IDLE: begin |
o_spi_req <= 1'b0; |
o_spi_wr <= 1'b0; |
if (i_req) // Need a clock to let the digestion |
ctstate <= `CT_NEXT; // process complete |
end |
`CT_NEXT: begin |
o_spi_wr <= 1'b1; |
o_spi_req <= 1'b1; |
o_spi_word <= ctcmd_word; |
o_spi_len <= ctcmd_len; |
o_spi_dir <= 1'b0; // Write |
if (accepted) |
begin |
ctstate <= (ctdat_skip)?`CT_WAIT_FOR_IDLE:`CT_DATA; |
o_bus_ack <= (ctdat_skip); |
o_data_ack <= (ctdat_skip)&&(ctbus_ack); |
end end |
`CT_GRANTED: begin |
o_spi_wr <= 1'b1; |
if ((accepted)&&(ctdat_skip)) |
ctstate <= `CT_WAIT_FOR_IDLE; |
else if (accepted)//&&(~ctdat_skip) |
ctstate <= `CT_DATA; |
end |
`CT_DATA: begin |
o_spi_wr <= 1'b1; |
o_spi_len <= ctdat_len; |
o_spi_dir <= ~ctdat_wr; |
o_spi_word <= ctdat_word; |
if (accepted) |
o_bus_ack <= 1'b1; |
if (accepted) |
ctstate <= (ctdat_wr)?`CT_WAIT_FOR_IDLE:`CT_READ_DATA; |
if ((accepted)&&(ctdat_wr)) |
o_data_ack <= 1'b1; |
end |
`CT_READ_DATA: begin |
o_spi_wr <= 1'b0; // No more words to go, just to wait |
o_spi_req <= 1'b1; |
if (i_spi_valid) // for a value to read |
begin |
o_data_ack <= 1'b1; |
o_data <= i_spi_data; |
ctstate <= `CT_WAIT_FOR_IDLE; |
end end |
default: begin // `CT_WAIT_FOR_IDLE |
o_spi_wr <= 1'b0; |
o_spi_req <= 1'b0; |
if (i_spi_stopped) |
ctstate <= `CT_IDLE; |
end |
endcase |
end |
|
// All of this is done in straight SPI mode, so our speed will always be zero |
assign o_spi_hold = 1'b0; |
assign o_spi_spd = 1'b0; |
|
endmodule |
|
`define ID_IDLE 5'h00 |
`define ID_WAIT_ON_START_ID 5'h01 |
`define ID_WAIT_ON_START_OTP 5'h02 |
`define ID_WAIT_ON_START_OTP_WRITE 5'h03 |
`define ID_READ_DATA_COMMAND 5'h04 |
`define ID_GET_DATA 5'h05 |
`define ID_LOADED 5'h06 |
`define ID_LOADED_NEXT 5'h07 |
`define ID_OTP_SEND_DUMMY 5'h08 |
`define ID_OTP_CLEAR 5'h09 |
`define ID_OTP_GET_DATA 5'h0a |
`define ID_OTP_WRITE 5'h0b |
`define ID_WAIT_ON_STOP 5'h0c |
`define ID_REQ_STATUS 5'h0d |
`define ID_REQ_STATUS_NEXT 5'h0e |
`define ID_READ_STATUS 5'h0f |
// |
`define ID_FINAL_STOP 5'h10 |
|
module idotpqspi(i_clk, i_req, i_wr, i_pipewr, i_addr, i_data, o_bus_ack, |
o_qspi_req, i_qspi_grant, |
o_spi_wr, o_spi_hold, o_spi_word, o_spi_len, |
o_spi_spd, o_spi_dir, i_spi_data, i_spi_valid, |
i_spi_busy, i_spi_stopped, o_data_ack, o_data, o_loaded, |
o_wip); |
input i_clk; |
input i_req, i_wr, i_pipewr; |
input [4:0] i_addr; |
input [31:0] i_data; |
output reg o_bus_ack, o_qspi_req; |
input i_qspi_grant; |
output reg o_spi_wr, o_spi_hold; |
output reg [31:0] o_spi_word; |
output reg [1:0] o_spi_len; |
output wire o_spi_spd; |
output reg o_spi_dir; |
input [31:0] i_spi_data; |
input i_spi_valid, i_spi_busy, i_spi_stopped; |
output reg o_data_ack; |
output reg [31:0] o_data; |
output wire o_loaded; |
output reg o_wip; |
|
reg id_loaded; |
initial id_loaded = 1'b0; |
assign o_loaded= id_loaded; |
|
/* |
// Only the ID register will be kept in memory, OTP will be read |
// or written upon request |
always @(posedge i_clk) |
if (i_addr[4]) |
o_data <= otpmem[i_addr[3:0]]; |
else |
o_data <= idmem[i_addr[2:0]]; |
|
always @(posedge i_clk) |
if ((otp_loaded)&&(i_req)&&(i_addr[4])) |
o_data_ack <= 1'b1; |
else if ((id_loaded)&&(i_req)&&(~i_addr[4])) |
o_data_ack <= idmem[i_addr[2:0]]; |
else |
o_data_ack <= 1'b0; |
*/ |
|
reg otp_read_request, id_read_request, accepted, otp_wr_request, |
id_read_device, last_id_read; |
reg [4:0] req_addr; |
reg [2:0] lcl_id_addr; |
reg [4:0] id_state; |
always @(posedge i_clk) |
begin |
otp_read_request <= (i_req)&&(~i_wr)&&((i_addr[4])||(i_addr[3:0]==4'hf)); |
last_id_read <= (i_req)&&(~i_addr[4])&&(i_addr[3:0]!=4'hf); |
id_read_request <= (i_req)&&(~i_addr[4])&&(i_addr[3:0]!=4'hf)&&(~last_id_read); |
id_read_device <= (i_req)&&(~i_addr[4])&&(i_addr[3:0]!=4'hf)&&(~id_loaded); |
accepted <= (~i_spi_busy)&&(i_qspi_grant)&&(o_spi_wr)&&(~accepted); |
|
otp_wr_request <= (i_req)&&(i_wr)&&((i_addr[4])||(i_addr[3:0]==4'hf)); |
|
if (id_state == `ID_IDLE) |
req_addr <= (i_addr[4:0]==5'h0f) ? 5'h10 |
: { 1'b0, i_addr[3:0] }; |
end |
|
reg last_addr; |
always @(posedge i_clk) |
last_addr <= (lcl_id_addr >= 3'h4); |
|
reg [31:0] idmem[0:5]; |
reg [31:0] r_data; |
|
// Now, quickly, let's deal with the fact that the data from the |
// bus comes one clock later ... |
reg nxt_data_ack, nxt_data_spi; |
reg [31:0] nxt_data; |
|
reg set_val, chk_wip; |
reg [2:0] set_addr; |
|
always @(posedge i_clk) |
begin // Depends upon state[4], otp_rd, otp_wr, otp_pipe, id_req, accepted, last_addr |
o_bus_ack <= 1'b0; |
// o_data_ack <= 1'b0; |
o_spi_hold <= 1'b0; |
nxt_data_ack <= 1'b0; |
nxt_data_spi <= 1'b0; |
chk_wip <= 1'b0; |
set_val <= 1'b0; |
if ((id_loaded)&&(id_read_request)) |
begin |
nxt_data_ack <= 1'b1; |
o_bus_ack <= 1'b1; |
end |
nxt_data <= idmem[i_addr[2:0]]; |
o_spi_wr <= 1'b0; // By default, we send nothing |
case(id_state) |
`ID_IDLE: begin |
o_qspi_req <= 1'b0; |
o_spi_dir <= 1'b0; // Write to SPI |
lcl_id_addr <= 3'h0; |
o_spi_word[23:7] <= 17'h00; |
o_spi_word[6:0] <= { req_addr[4:0], 2'b00 }; |
r_data <= i_data; |
o_wip <= 1'b0; |
if (otp_read_request) |
begin |
// o_spi_word <= { 8'h48, 8'h00, 8'h00, 8'h00 }; |
id_state <= `ID_WAIT_ON_START_OTP; |
o_bus_ack <= 1'b1; |
end else if (otp_wr_request) |
begin |
o_bus_ack <= 1'b1; |
// o_data_ack <= 1'b1; |
nxt_data_ack <= 1'b1; |
id_state <= `ID_WAIT_ON_START_OTP_WRITE; |
end else if (id_read_device) |
begin |
id_state <= `ID_WAIT_ON_START_ID; |
o_bus_ack <= 1'b0; |
o_spi_word[31:24] <= 8'h9f; |
end end |
`ID_WAIT_ON_START_ID: begin |
o_spi_wr <= 1'b1; |
o_qspi_req <= 1'b1; |
o_spi_len <= 2'b0; // 8 bits |
if (accepted) |
id_state <= `ID_READ_DATA_COMMAND; |
end |
`ID_WAIT_ON_START_OTP: begin |
o_spi_wr <= 1'b1; |
o_spi_word[31:24] <= 8'h4B; |
o_qspi_req <= 1'b1; |
o_spi_len <= 2'b11; // 32 bits |
o_spi_word[6:0] <= { req_addr[4:0], 2'b00 }; |
if (accepted) // Read OTP command was just sent |
id_state <= `ID_OTP_SEND_DUMMY; |
end |
`ID_WAIT_ON_START_OTP_WRITE: begin |
o_spi_wr <= 1'b1; |
o_qspi_req <= 1'b1; |
o_wip <= 1'b1; |
o_spi_len <= 2'b11; // 32 bits |
o_spi_word[31:24] <= 8'h42; |
if (accepted) // Read OTP command was just sent |
id_state <= `ID_OTP_WRITE; |
end |
`ID_READ_DATA_COMMAND: begin |
o_spi_len <= 2'b11; // 32-bits |
o_spi_wr <= (~last_addr); // Still transmitting |
o_spi_dir <= 1'b1; // Read from SPI |
o_qspi_req <= 1'b1; |
if (accepted) |
id_state <= `ID_GET_DATA; |
end |
`ID_GET_DATA: begin |
o_spi_len <= 2'b11; // 32-bits |
o_spi_wr <= (~last_addr); // Still transmitting |
o_spi_dir <= 1'b1; // Read from SPI |
o_qspi_req <= 1'b1; |
if (i_spi_valid) // same as accepted |
begin |
set_val <= 1'b1; |
set_addr <= lcl_id_addr[2:0]; |
// idmem[lcl_id_addr[2:0]] <= i_spi_data; |
lcl_id_addr <= lcl_id_addr + 3'h1; |
if (last_addr) |
id_state <= `ID_LOADED; |
end end |
`ID_LOADED: begin |
id_loaded <= 1'b1; |
o_bus_ack <= 1'b1; |
o_spi_wr <= 1'b0; |
nxt_data_ack <= 1'b1; |
id_state <= `ID_LOADED_NEXT; |
end |
`ID_LOADED_NEXT: begin |
o_spi_len <= 2'b11; // 32-bits |
o_bus_ack <= 1'b0; |
o_spi_wr <= 1'b0; |
nxt_data_ack <= 1'b1; |
id_state <= `ID_IDLE; |
end |
`ID_OTP_SEND_DUMMY: begin |
o_spi_len <= 2'b00; // 1 byte |
o_spi_wr <= 1'b1; // Still writing |
o_spi_dir <= 1'b0; // Write to SPI |
if (accepted) // Wait for the command to be accepted |
id_state <= `ID_OTP_CLEAR; |
end |
`ID_OTP_CLEAR: begin |
o_spi_wr <= 1'b1; // Still writing |
o_spi_dir <= 1'b1; // Read from SPI |
o_spi_len <= 2'b11; // Read from SPI |
if (accepted) |
id_state <= `ID_OTP_GET_DATA; |
end |
`ID_OTP_GET_DATA: begin |
if (i_spi_valid) |
begin |
id_state <= `ID_FINAL_STOP; |
nxt_data_ack <= 1'b1; |
nxt_data_spi <= 1'b1; |
end end |
`ID_OTP_WRITE: begin |
o_spi_wr <= 1'b1; |
o_spi_len <= 2'b11; |
o_spi_dir <= 1'b0; // Write to SPI |
o_spi_word <= r_data; |
// o_bus_ack <= (otp_wr_request)&&(accepted)&&(i_pipewr); |
// o_data_ack <= (otp_wr_request)&&(accepted); |
if (accepted) // &&(~i_pipewr) |
id_state <= `ID_WAIT_ON_STOP; |
else if(accepted) |
begin |
o_spi_word <= i_data; |
r_data <= i_data; |
end end |
`ID_WAIT_ON_STOP: begin |
o_spi_wr <= 1'b0; |
if (i_spi_stopped) |
id_state <= `ID_REQ_STATUS; |
end |
`ID_REQ_STATUS: begin |
o_spi_wr <= 1'b1; |
o_spi_hold <= 1'b0; |
o_spi_word[31:24] <= 8'h05; |
o_spi_dir <= 1'b0; |
o_spi_len <= 2'b00; |
if (accepted) |
id_state <= `ID_REQ_STATUS_NEXT; |
end |
`ID_REQ_STATUS_NEXT: begin |
o_spi_wr <= 1'b1; |
o_spi_hold <= 1'b0; |
o_spi_dir <= 1'b1; // Read |
o_spi_len <= 2'b00; // 8 bits |
// o_spi_word <= dont care |
if (accepted) |
id_state <= `ID_READ_STATUS; |
end |
`ID_READ_STATUS: begin |
o_spi_wr <= 1'b1; |
o_spi_hold <= 1'b0; |
o_spi_dir <= 1'b1; // Read |
o_spi_len <= 2'b00; // 8 bits |
// o_spi_word <= dont care |
if (i_spi_valid) |
chk_wip <= 1'b1; |
if ((chk_wip)&&(~i_spi_data[0])) |
begin |
o_wip <= 1'b0; |
id_state <= `ID_FINAL_STOP; |
end end |
default: begin // ID_FINAL_STOP |
o_bus_ack <= 1'b0; |
nxt_data_ack <= 1'b0; |
o_qspi_req <= 1'b0; |
o_spi_wr <= 1'b0; |
o_spi_hold <= 1'b0; |
o_spi_dir <= 1'b1; // Read |
o_spi_len <= 2'b00; // 8 bits |
// o_spi_word <= dont care |
if (i_spi_stopped) |
id_state <= `ID_IDLE; |
end |
endcase |
end |
|
always @(posedge i_clk) |
begin |
if (nxt_data_ack) |
o_data <= (nxt_data_spi)?i_spi_data : nxt_data; |
o_data_ack <= nxt_data_ack; |
end |
|
always @(posedge i_clk) |
if (set_val) |
idmem[set_addr] <= i_spi_data; |
|
assign o_spi_spd = 1'b0; // Slow, 1-bit at a time |
|
endmodule |
|
|
|
/rtl/wbufifo.v
0,0 → 1,132
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbufifo.v |
// |
// Project: FPGA library |
// |
// Purpose: This was once a FIFO for a UART ... but now it works as a |
// synchronous FIFO for JTAG-wishbone conversion 36-bit codewords. |
// |
// |
// 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. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
module wbufifo(i_clk, i_rst, i_wr, i_data, i_rd, o_data, o_empty_n, o_err); |
parameter BW=66, LGFLEN=10, FLEN=(1<<LGFLEN); |
input i_clk, i_rst; |
input i_wr; |
input [(BW-1):0] i_data; |
input i_rd; |
output reg [(BW-1):0] o_data; |
output reg o_empty_n; |
output wire o_err; |
|
reg [(BW-1):0] fifo[0:(FLEN-1)]; |
reg [(LGFLEN-1):0] r_first, r_last; |
|
reg will_overflow; |
initial will_overflow = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
will_overflow <= 1'b0; |
else if (i_rd) |
will_overflow <= (will_overflow)&&(i_wr); |
else if (i_wr) |
will_overflow <= (r_first+2 == r_last); |
else if (r_first+1 == r_last) |
will_overflow <= 1'b1; |
|
// Write |
initial r_first = 0; |
always @(posedge i_clk) |
if (i_rst) |
r_first <= { (LGFLEN){1'b0} }; |
else if (i_wr) |
begin // Cowardly refuse to overflow |
if ((i_rd)||(~will_overflow)) // (r_first+1 != r_last) |
r_first <= r_first+{{(LGFLEN-1){1'b0}},1'b1}; |
// else o_ovfl <= 1'b1; |
end |
always @(posedge i_clk) |
if (i_wr) // Write our new value regardless--on overflow or not |
fifo[r_first] <= i_data; |
|
// Reads |
// Following a read, the next sample will be available on the |
// next clock |
// Clock ReadCMD ReadAddr Output |
// 0 0 0 fifo[0] |
// 1 1 0 fifo[0] |
// 2 0 1 fifo[1] |
// 3 0 1 fifo[1] |
// 4 1 1 fifo[1] |
// 5 1 2 fifo[2] |
// 6 0 3 fifo[3] |
// 7 0 3 fifo[3] |
reg will_underflow; |
initial will_underflow = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
will_underflow <= 1'b0; |
else if (i_wr) |
will_underflow <= (will_underflow)&&(i_rd); |
else if (i_rd) |
will_underflow <= (r_last+1==r_first); |
else |
will_underflow <= (r_last == r_first); |
|
initial r_last = 0; |
always @(posedge i_clk) |
if (i_rst) |
r_last <= { (LGFLEN){1'b0} }; |
else if (i_rd) |
begin |
if ((i_wr)||(~will_underflow)) // (r_first != r_last) |
r_last <= r_last+{{(LGFLEN-1){1'b0}},1'b1}; |
// Last chases first |
// Need to be prepared for a possible two |
// reads in quick succession |
// o_data <= fifo[r_last+1]; |
// else o_unfl <= 1'b1; |
end |
always @(posedge i_clk) |
o_data <= fifo[(i_rd)?(r_last+{{(LGFLEN-1){1'b0}},1'b1}) |
:(r_last)]; |
|
wire [(LGFLEN-1):0] nxt_first; |
assign nxt_first = r_first+{{(LGFLEN-1){1'b0}},1'b1}; |
assign o_err = ((i_wr)&&(will_overflow)&&(~i_rd)) |
||((i_rd)&&(will_underflow)&&(~i_wr)); |
|
// wire [(LGFLEN-1):0] fill; |
// assign fill = (r_first-r_last); |
wire [(LGFLEN-1):0] nxt_last; |
assign nxt_last = r_last+{{(LGFLEN-1){1'b0}},1'b1}; |
always @(posedge i_clk) |
if (i_rst) |
o_empty_n <= 1'b0; |
else |
o_empty_n <= (~i_rd)&&(r_first != r_last) |
||(i_rd)&&(r_first != nxt_last); |
endmodule |
/rtl/rtcgps.v
0,0 → 1,464
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: rtcgps.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: Implement a real time clock, including alarm, count--down |
// timer, stopwatch, variable time frequency, and more. |
// |
// This particular version has hooks for a GPS 1PPS, as well as a |
// finely tracked clock speed output, to allow for fine clock precision |
// and good freewheeling even if/when GPS is lost. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 rtcgps(i_clk, |
// Wishbone interface |
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, i_wb_data, |
// o_wb_ack, o_wb_stb, o_wb_data, // no reads here |
// Output registers |
o_data, // multiplexed based upon i_wb_addr |
// Output controls |
o_interrupt, |
// A once-per-day strobe on the last clock of the day |
o_ppd, |
// GPS interface |
i_gps_valid, i_gps_pps, i_gps_ckspeed, |
// Our personal timing, for debug purposes |
o_rtc_pps); |
parameter DEFAULT_SPEED = 32'd2814750; //2af31e = 2^48 / 100e6 MHz |
input i_clk; |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [1:0] i_wb_addr; |
input [31:0] i_wb_data; |
// input i_btn; |
output reg [31:0] o_data; |
output wire o_interrupt, o_ppd; |
// GPS interface |
input i_gps_valid, i_gps_pps; |
input [31:0] i_gps_ckspeed; |
// Personal PPS |
output wire o_rtc_pps; |
|
reg [21:0] clock; |
reg [31:0] stopwatch, ckspeed; |
reg [25:0] timer; |
|
reg ck_wr, tm_wr, sw_wr, al_wr, r_data_zero_byte; |
reg [25:0] r_data; |
always @(posedge i_clk) |
begin |
ck_wr <= ((i_wb_stb)&&(i_wb_addr==2'b00)&&(i_wb_we)); |
tm_wr <= ((i_wb_stb)&&(i_wb_addr==2'b01)&&(i_wb_we)); |
sw_wr <= ((i_wb_stb)&&(i_wb_addr==2'b10)&&(i_wb_we)); |
al_wr <= ((i_wb_stb)&&(i_wb_addr==2'b11)&&(i_wb_we)); |
r_data <= i_wb_data[25:0]; |
r_data_zero_byte <= (i_wb_data[7:0] == 8'h00); |
end |
|
reg [39:0] ck_counter; |
reg ck_carry, ck_sub_carry; |
always @(posedge i_clk) |
if ((i_gps_valid)&&(i_gps_pps)) |
begin |
ck_carry <= 0; |
// Start our counter 2 clocks into the future. |
// Why? Because if we hit the PPS, we'll be delayed |
// one clock from true time. This (hopefully) locks |
// us back onto true time. Further, if we end up |
// off (i.e., go off before the GPS tick ...) then |
// the GPS tick will put us back on track ... likewise |
// we've got code following that should keep us from |
// ever producing two PPS's per second. |
ck_counter <= { 7'h00, ckspeed, 1'b0 }; |
ck_sub_carry <= ckspeed[31]; |
end else begin |
{ ck_sub_carry, ck_counter[31:0] } |
<= ck_counter[31:0] + ckspeed; |
{ ck_carry, ck_counter[39:32] } |
<= ck_counter[39:32] + { 7'h0, ck_sub_carry }; |
end |
|
reg ck_pps; |
reg ck_ppm, ck_pph, ck_ppd; |
reg [7:0] ck_sub; |
initial clock = 22'h00000000; |
always @(posedge i_clk) |
if ((i_gps_pps)&&(i_gps_valid)&&(ck_sub[7])) |
ck_pps <= 1'b1; |
else if ((ck_carry)&&(ck_sub == 8'hff)) |
ck_pps <= 1'b1; |
else |
ck_pps <= 1'b0; |
|
reg [6:0] next_clock_secs; |
always @(posedge i_clk) |
begin |
next_clock_secs[3:0] <= (clock[3:0] >= 4'h9) ? 4'h0 // clk 1 |
: (clock[3:0] + 4'h1); |
next_clock_secs[6:4] <= (ck_ppm) ? 3'h0 // clk 2 |
: (clock[3:0] >= 4'h9) |
? (clock[6:4] + 3'h1) |
: clock[6:4]; |
end |
|
reg [6:0] next_clock_mins; |
always @(posedge i_clk) |
begin |
next_clock_mins[3:0] <= (clock[11:8] >= 4'h9) ? 4'h0 |
: (clock[11:8] + 4'h1); |
next_clock_mins[6:4] <= (ck_pph) ? 3'h0 |
: (clock[11:8] >= 4'h9) |
? (clock[14:12] + 3'h1) |
: clock[14:12]; |
end |
|
reg [5:0] next_clock_hrs; |
always @(posedge i_clk) |
begin |
next_clock_hrs[3:0] <= (clock[19:16] >= 4'h9) ? 4'h0 |
: (clock[19:16] + 4'h1); |
next_clock_hrs[5:4] <= (ck_ppd) ? 2'h0 |
: (clock[19:16] >= 4'h9) |
? (clock[21:20] + 2'h1) |
: (clock[21:20]); |
end |
|
reg [4:0] ck_pending; |
assign o_rtc_pps = ck_pps; |
always @(posedge i_clk) |
begin |
if ((i_gps_valid)&&(i_gps_pps)) |
ck_sub <= 0; |
else if (ck_carry) |
ck_sub <= ck_sub + 1; |
|
if ((ck_pps)&&(~ck_pending[4])) // advance the seconds |
clock[6:0] <= next_clock_secs; |
clock[7] <= 1'b0; |
ck_ppm <= (clock[6:0] == 7'h59); |
|
if ((ck_pps)&&(ck_ppm)&&(~ck_pending[4])) // advance the minutes |
clock[14:8] <= next_clock_mins; |
clock[15] <= 1'b0; |
ck_pph <= (clock[14:8] == 7'h59)&&(ck_ppm); |
|
if ((ck_pps)&&(ck_pph)&&(~ck_pending[4])) // advance the hours |
clock[21:16] <= next_clock_hrs; |
ck_ppd <= (clock[21:16] == 6'h23)&&(ck_pph); |
|
clock[ 7] <= 1'b0; |
clock[15] <= 1'b0; |
|
if (ck_wr) |
begin |
if (~r_data[7]) |
clock[6:0] <= i_wb_data[6:0]; |
if (~r_data[15]) |
clock[14:8] <= i_wb_data[14:8]; |
if (~r_data[22]) |
clock[21:16] <= i_wb_data[21:16]; |
if ((~i_gps_valid)&&(r_data_zero_byte)) |
ck_sub <= 8'h00; |
ck_pending <= 5'h1f; |
end else |
ck_pending <= { ck_pending[3:0], 1'b0 }; |
end |
|
reg [21:0] ck_last_clock; |
always @(posedge i_clk) |
ck_last_clock <= clock[21:0]; |
|
|
|
// |
reg [23:0] next_timer; |
reg ztimer; |
reg [4:0] tmr_carry; |
always @(posedge i_clk) |
begin |
tmr_carry[0] <= (timer[ 3: 0]== 4'h0); |
tmr_carry[1] <= (timer[ 6: 4]== 3'h0)&&(tmr_carry[0]); |
tmr_carry[2] <= (timer[11: 8]== 4'h0)&&(tmr_carry[1]); |
tmr_carry[3] <= (timer[14:12]== 3'h0)&&(tmr_carry[2]); |
tmr_carry[4] <= (timer[19:16]== 4'h0)&&(tmr_carry[3]); |
ztimer <= (timer[23:0]== 24'h0); |
|
// Keep unused bits at zero |
next_timer <= 24'h00; |
// Seconds |
next_timer[ 3: 0] <= (tmr_carry[0])? 4'h9: (timer[ 3: 0]-4'h1); |
next_timer[ 6: 4] <= (tmr_carry[1])? 3'h5: (timer[ 6: 4]-3'h1); |
// Minutes |
next_timer[11: 8] <= (tmr_carry[2])? 4'h9: (timer[11: 8]-4'h1); |
next_timer[14:12] <= (tmr_carry[3])? 3'h5: (timer[14:12]-3'h1); |
// Hours |
next_timer[19:16] <= (tmr_carry[4])? 4'h9: (timer[19:16]-4'h1); |
next_timer[23:20] <= (timer[23:20]-4'h1); |
end |
|
reg new_timer, new_timer_set, new_timer_last, tm_pending_start; |
reg [23:0] new_timer_val; |
|
reg tm_pps, tm_ppm, tm_int; |
wire tm_stopped, tm_running, tm_alarm; |
assign tm_stopped = ~timer[24]; |
assign tm_running = timer[24]; |
assign tm_alarm = timer[25]; |
reg [23:0] tm_start; |
reg [7:0] tm_sub; |
initial tm_start = 24'h00; |
initial timer = 26'h00; |
initial tm_int = 1'b0; |
initial tm_pps = 1'b0; |
initial tm_pending_start = 1'b0; |
always @(posedge i_clk) |
begin |
if (ck_carry) |
begin |
tm_sub <= tm_sub + 1; |
tm_pps <= (tm_sub == 8'hff); |
end else |
tm_pps <= 1'b0; |
|
if (new_timer_set) // Conclude a write |
timer[23:0] <= new_timer_val; |
else if ((~tm_alarm)&&(tm_running)&&(tm_pps)) |
begin // Otherwise, if we are running ... |
timer[25] <= 1'b0; // Clear any alarm |
if (ztimer) // unless we've hit zero |
timer[25] <= 1'b1; |
else if (~new_timer) |
timer[23:0] <= next_timer; |
end |
|
tm_int <= (tm_running)&&(tm_pps)&&(~tm_alarm)&&(ztimer); |
|
if (tm_alarm) // Stop the timer on an alarm |
timer[24] <= 1'b0; |
|
new_timer <= 1'b0; |
tm_pending_start <= 1'b0; |
if ((tm_wr)&&(tm_running)) // Writes while running |
// Only allow the timer to stop, nothing more |
timer[24] <= r_data[24]; |
else if ((tm_wr)&&(tm_stopped)) // Writes while off |
begin |
// We're going to pipeline this change by a couple |
// of clocks, to get it right |
new_timer <= 1'b1; |
new_timer_val <= r_data[23:0]; |
tm_pending_start <= r_data[24]; |
|
// Still ... any write clears the alarm |
timer[25] <= 1'b0; |
end |
|
new_timer_set <= (new_timer)&&(new_timer_val != 24'h000); |
new_timer_last <= (new_timer)&&(new_timer_val == 24'h000); |
if (new_timer_last) |
begin |
new_timer_val <= tm_start; |
tm_sub <= 8'h00; |
new_timer_set <= 1'b1; |
tm_pending_start <= 1'b1; |
end else if (new_timer_set) |
begin |
tm_start <= new_timer_val; |
tm_sub <= 8'h00; |
tm_pending_start <= 1'b1; |
timer[24] <= 1'b1; |
end |
end |
|
// |
// Stopwatch functionality |
// |
// Setting bit '0' starts the stop watch, clearing it stops it. |
// Writing to the register with bit '1' high will clear the stopwatch, |
// and return it to zero provided that the stopwatch is stopped either |
// before or after the write. Hence, writing a '2' to the device |
// will always stop and clear it, whereas writing a '3' to the device |
// will only clear it if it was already stopped. |
reg [6:0] next_sw_secs; |
always @(posedge i_clk) |
begin |
next_sw_secs[3:0] <= (stopwatch[11:8] >= 4'h9) ? 4'h0 |
: (stopwatch[11:8] + 4'h1); |
next_sw_secs[6:4] <= (stopwatch[14:8] == 7'h59) ? 3'h0 |
: (stopwatch[11:8] == 4'h9) |
? (stopwatch[14:12]+3'h1) |
: stopwatch[14:12]; |
end |
|
reg [6:0] next_sw_mins; |
always @(posedge i_clk) |
begin |
next_sw_mins[3:0] <= (stopwatch[19:16] >= 4'h9) ? 4'h0 |
: (stopwatch[19:16] + 4'h1); |
next_sw_mins[6:4] <= (stopwatch[22:16] == 7'h59) ? 3'h0 |
: (stopwatch[19:16]==4'h9) |
? (stopwatch[22:20]+3'h1) |
: stopwatch[22:20]; |
end |
|
reg [5:0] next_sw_hrs; |
always @(posedge i_clk) |
begin |
next_sw_hrs[3:0] <= (stopwatch[27:24] >= 4'h9) ? 4'h0 |
: (stopwatch[27:24] + 4'h1); |
next_sw_hrs[5:4] <= (stopwatch[29:24] >= 6'h23) ? 2'h0 |
: (stopwatch[27:24]==4'h9) |
? (stopwatch[29:28]+2'h1) |
: stopwatch[29:28]; |
end |
|
reg sw_pps, sw_ppm, sw_pph; |
reg [7:0] sw_sub; |
wire sw_running; |
assign sw_running = stopwatch[0]; |
initial stopwatch = 32'h00000; |
always @(posedge i_clk) |
begin |
sw_pps <= 1'b0; |
if ((sw_running)&&(ck_carry)) |
begin |
sw_sub <= sw_sub + 1; |
sw_pps <= (sw_sub == 8'hff); |
end |
|
stopwatch[7:1] <= sw_sub[7:1]; |
|
if (sw_pps) // Second hand |
stopwatch[14:8] <= next_sw_secs; |
sw_ppm <= (stopwatch[14:8] == 7'h59); |
|
if ((sw_pps)&&(sw_ppm)) // Minutes |
stopwatch[22:16] <= next_sw_mins; |
sw_pph <= (stopwatch[23:16] == 8'h59)&&(sw_ppm); |
|
if ((sw_pps)&&(sw_pph)) // And hours |
stopwatch[29:24] <= next_sw_hrs; |
|
if (sw_wr) |
begin |
stopwatch[0] <= r_data[0]; |
if((r_data[1])&&((~stopwatch[0])||(~r_data[0]))) |
begin |
stopwatch[31:1] <= 31'h00; |
sw_sub <= 8'h00; |
sw_pps <= 1'b0; |
sw_ppm <= 1'b0; |
sw_pph <= 1'b0; |
end |
end |
end |
|
// |
// The alarm code |
// |
// Set the alarm register to the time you wish the board to "alarm". |
// The "alarm" will take place once per day at that time. At that |
// time, the RTC code will generate a clock interrupt, and the CPU/host |
// can come and see that the alarm tripped. |
// |
// |
reg [21:0] alarm_time; |
reg al_int, // The alarm interrupt line |
al_enabled, // Whether the alarm is enabled |
al_tripped; // Whether the alarm has tripped |
initial al_enabled= 1'b0; |
initial al_tripped= 1'b0; |
always @(posedge i_clk) |
begin |
if (al_wr) |
begin |
// Only adjust the alarm hours if the requested hours |
// are valid. This allows writes to the register, |
// without a prior read, to leave these configuration |
// bits alone. |
if (r_data[21:20] != 2'h3) |
alarm_time[21:16] <= i_wb_data[21:16]; |
// Here's the same thing for the minutes: only adjust |
// the alarm minutes if the new bits are not all 1's. |
if (~r_data[15]) |
alarm_time[15:8] <= i_wb_data[15:8]; |
// Here's the same thing for the seconds: only adjust |
// the alarm seconds if the new bits are not all 1's. |
if (~r_data[7]) |
alarm_time[7:0] <= i_wb_data[7:0]; |
al_enabled <= i_wb_data[24]; |
// Reset the alarm if a '1' is written to the tripped |
// register, or if the alarm is disabled. |
if ((r_data[25])||(~r_data[24])) |
al_tripped <= 1'b0; |
end |
|
al_int <= ((ck_last_clock != alarm_time) |
&&(clock[21:0] == alarm_time)&&(al_enabled)); |
if (al_int) |
al_tripped <= 1'b1; |
end |
|
// |
// The ckspeed register is equal to 2^48 divded by the number of |
// clock ticks you expect per second. Adjust high for a slower |
// clock, lower for a faster clock. In this fashion, a single |
// real time clock RTL file can handle tracking the clock in any |
// device. Further, because this is only the lower 32 bits of a |
// 48 bit counter per seconds, the clock jitter is kept below |
// 1 part in 65 thousand. |
// |
initial ckspeed = DEFAULT_SPEED; |
// In the case of verilator, comment the above and uncomment the line |
// below. The clock constant below is "close" to simulation time, |
// meaning that my verilator simulation is running about 300x slower |
// than board time. |
// initial ckspeed = 32'd786432000; |
always @(posedge i_clk) |
if (i_gps_valid) |
ckspeed <= i_gps_ckspeed; |
|
assign o_interrupt = tm_int || al_int; |
|
// A once-per day strobe, on the last second of the day so that the |
// the next clock is the first clock of the day. This is useful for |
// connecting this module to a year/month/date date/calendar module. |
assign o_ppd = (ck_ppd)&&(ck_pps); |
|
always @(posedge i_clk) |
case(i_wb_addr) |
2'b00: o_data <= { ~i_gps_valid, 7'h0, 2'b00, clock[21:0] }; |
2'b01: o_data <= { 6'h00, timer }; |
2'b10: o_data <= stopwatch; |
2'b11: o_data <= { 6'h00, al_tripped, al_enabled, 2'b00, alarm_time }; |
endcase |
|
endmodule |
/rtl/wbuexec.v
0,0 → 1,362
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbuexec.v |
// |
// Project: FPGA library |
// |
// Purpose: This is the part of the USB-JTAG to wishbone conversion that |
// actually conducts a wishbone transaction. Transactions are |
// requested via codewords that come in, and the results recorded on |
// codewords that are sent out. Compression and/or decompression, coding |
// etc. all take place external to this routine. |
// |
// |
// 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. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`define WB_IDLE 3'b000 |
`define WB_READ_REQUEST 3'b001 |
`define WB_WRITE_REQUEST 3'b010 |
`define WB_ACK 3'b011 |
`define WB_WAIT_ON_NEXT_WRITE 3'b100 |
`define WB_FLUSH_WRITE_REQUESTS 3'b101 |
|
module wbuexec(i_clk, i_rst, i_stb, i_codword, o_busy, |
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_stb, o_codword); |
input i_clk, i_rst; |
// The command inputs |
input i_stb; |
input [35:0] i_codword; |
output wire o_busy; |
// Wishbone outputs |
output reg o_wb_cyc; |
output reg o_wb_stb; |
output reg o_wb_we; |
output reg [31:0] o_wb_addr, o_wb_data; |
// Wishbone inputs |
input i_wb_ack, i_wb_stall, i_wb_err; |
input [31:0] i_wb_data; |
// And our codeword outputs |
output reg o_stb; |
output reg [35:0] o_codword; |
// output wire o_dbg; |
|
|
wire w_accept, w_eow, w_newwr, w_new_err; |
// wire w_newad, w_newrd; |
assign w_accept = (i_stb)&&(~o_busy); |
// assign w_newad = (w_accept)&&(i_codword[35:34] == 2'b00); |
assign w_newwr = (w_accept)&&(i_codword[35:34] == 2'b01); |
assign w_eow = (w_accept)&&(i_codword[35:30] == 6'h2e); |
// assign w_newrd = (w_accept)&&(i_codword[35:34] == 2'b11); |
wire [31:0] w_cod_data; |
assign w_cod_data={ i_codword[32:31], i_codword[29:0] }; |
assign w_new_err = ((w_accept) |
&&(i_codword[35:33] != 3'h3) |
&&(i_codword[35:30] != 6'h2e)); |
|
reg [2:0] wb_state; |
reg [9:0] r_acks_needed, r_len; |
reg r_inc, r_new_addr, last_read_request, last_ack, zero_acks; |
reg single_read_request; |
|
initial r_new_addr = 1'b1; |
initial wb_state = `WB_IDLE; |
initial o_stb = 1'b0; |
always @(posedge i_clk) |
if (i_rst) |
begin |
wb_state <= `WB_IDLE; |
o_stb <= 1'b1; |
o_codword <= { 6'h3, i_wb_data[29:0] }; // BUS Reset |
o_wb_cyc <= 1'b0; |
o_wb_stb <= 1'b0; |
end else case(wb_state) |
`WB_IDLE: begin |
o_wb_cyc <= 1'b0; |
o_wb_stb <= 1'b0; |
// Now output codewords while we're idle, |
// ... unless we get an address command (later). |
o_stb <= 1'b0; |
|
// The new instruction. The following |
// don't matter if we're not running, |
// so set them any time in this state, |
// and if we move then they'll still be |
// set right. |
// |
// Increment addresses? |
r_inc <= i_codword[30]; |
// Will this be a write? |
o_wb_we <= (~i_codword[35]); |
// |
// Our next codeword will be the new address (if there |
// is one). Set it here. The o_stb line will determine |
// if this codeword is actually sent out. |
// |
o_codword <= { 4'h2, o_wb_addr }; |
o_wb_we <= (i_codword[35:34] != 2'b11); |
// |
// The output data is a don't care, unless we are |
// starting a write. Hence, let's always set it as |
// though we were about to start a write. |
// |
o_wb_data <= w_cod_data; |
// |
if (i_stb) |
begin |
// Default is not to send any codewords |
// Do we need to broadcast a new address? |
// r_new_addr <= 1'b0; |
// |
casez(i_codword[35:32]) |
4'b0000: begin // Set a new (arbitrary) address |
// r_new_addr <= 1'b1; |
o_wb_addr <= i_codword[31:0]; //w_cod_data |
end |
4'b001?: begin // Set a new relative address |
// r_new_addr <= 1'b1; |
o_wb_addr <= o_wb_addr // + w_cod_data; |
|
+ { i_codword[32:31], i_codword[29:0] }; |
end |
4'b01??: begin // Start a write transaction, |
// address is alrdy set |
// r_new_addr <= 1'b1; |
wb_state <= `WB_WRITE_REQUEST; |
o_wb_cyc <= 1'b1; |
o_wb_stb <= 1'b1; |
end |
4'b11??: begin // Start a vector read |
// Address is already set ... |
// This also depends upon the decoder working |
if (r_new_addr) |
o_stb <= 1'b1; |
wb_state <= `WB_READ_REQUEST; |
o_wb_cyc <= 1'b1; |
o_wb_stb <= 1'b1; |
end |
default: |
; |
endcase |
end end |
`WB_READ_REQUEST: begin |
o_wb_cyc <= 1'b1; |
o_wb_stb <= 1'b1; |
|
if (i_wb_err) |
wb_state <= `WB_IDLE; |
|
o_stb <= (i_wb_err)||(i_wb_ack); |
|
if (i_wb_err) // Bus Error |
o_codword <= { 6'h5, i_wb_data[29:0] }; |
else // Read data on ack |
o_codword <= { 3'h7, i_wb_data[31:30], r_inc, |
i_wb_data[29:0] }; |
|
if ((r_inc)&&(~i_wb_stall)) |
o_wb_addr <= o_wb_addr + 32'h001; |
|
|
if (~i_wb_stall) // Deal with the strobe line |
begin // Strobe was accepted, busy should be '1' here |
if ((single_read_request)||(last_read_request)) // (r_len != 0) // read |
begin |
wb_state <= `WB_ACK; |
o_wb_stb <= 1'b0; |
end |
end end |
`WB_WRITE_REQUEST: begin |
o_wb_cyc <= 1'b1; |
o_wb_stb <= 1'b1; |
// |
|
if (i_wb_err) // Bus Err |
o_codword <= { 6'h5, i_wb_data[29:0] }; |
else // Write acknowledgement |
o_codword <= { 6'h2, i_wb_data[29:0] }; |
|
if ((r_inc)&&(~i_wb_stall)) |
o_wb_addr <= o_wb_addr + 32'h001; |
|
o_stb <= (i_wb_err)||(~i_wb_stall); |
|
// Don't need to worry about accepting anything new |
// here, since we'll always be busy while in this state. |
// Hence, we cannot accept new write requests. |
// |
|
if (i_wb_err) |
begin |
wb_state <= `WB_FLUSH_WRITE_REQUESTS; |
// |
o_wb_cyc <= 1'b0; |
o_wb_stb <= 1'b0; |
end else if (~i_wb_stall) |
begin |
wb_state <= `WB_WAIT_ON_NEXT_WRITE; |
o_wb_stb <= 1'b0; |
end end |
`WB_ACK: begin |
o_wb_cyc <= 1'b1; |
o_wb_stb <= 1'b0; |
// |
// No strobes are being sent out. No further |
// bus transactions are requested. We only need |
// to finish processing the last one(s) by waiting |
// for (and recording?) their acks. |
// |
// Process acknowledgements |
if (i_wb_err) // Bus error |
o_codword <= { 6'h5, i_wb_data[29:0] }; |
else // Read data |
o_codword <= { 3'h7, i_wb_data[31:30], r_inc, |
i_wb_data[29:0] }; |
|
// Return a read result, or (possibly) an error |
// notification |
o_stb <= (((i_wb_ack)&&(~o_wb_we)) || (i_wb_err)); |
|
if (((last_ack)&&(i_wb_ack))||(zero_acks)||(i_wb_err)) |
begin |
o_wb_cyc <= 1'b0; |
wb_state <= `WB_IDLE; |
end end |
`WB_WAIT_ON_NEXT_WRITE: begin |
|
o_codword <= { 6'h5, i_wb_data[29:0] }; |
o_stb <= (i_wb_err)||(w_new_err); |
|
o_wb_data <= w_cod_data; |
o_wb_cyc <= 1'b1; |
o_wb_stb <= 1'b0; |
|
if (w_new_err) // Something other than a write or EOW |
begin |
o_wb_cyc <= 1'b0; |
wb_state <= `WB_IDLE; |
end else if (i_wb_err) // Bus returns an error |
begin |
o_wb_cyc <= 1'b0; |
wb_state <= `WB_FLUSH_WRITE_REQUESTS; |
end |
else if (w_newwr) // Need to make a new write request |
begin |
wb_state <= `WB_WRITE_REQUEST; |
o_wb_stb <= 1'b1; |
end |
else if (w_eow) // All done writing, wait for last ack |
wb_state <= `WB_ACK; |
end |
`WB_FLUSH_WRITE_REQUESTS: begin |
// We come in here after an error within a write |
// We need to wait until the command cycle finishes |
// issuing all its write commands before we can go back |
// to idle. |
// |
// In the off chance that we are in here in error, or |
// out of sync, we'll transition to WB_IDLE and just |
// issue a second error token. |
|
o_wb_cyc <= 1'b0; |
o_wb_stb <= 1'b0; |
o_codword <= { 6'h5, i_wb_data[29:0] }; |
o_stb <= (w_new_err); |
|
if ((w_eow)||(w_new_err)) |
wb_state <= `WB_IDLE; |
end |
default: begin |
o_stb <= 1'b1; |
o_codword <= { 6'h3, i_wb_data[29:0] }; |
wb_state <= `WB_IDLE; |
o_wb_cyc <= 1'b0; |
o_wb_stb <= 1'b0; |
end |
endcase |
|
assign o_busy = (wb_state != `WB_IDLE) |
&&(wb_state != `WB_WAIT_ON_NEXT_WRITE) |
&&(wb_state != `WB_FLUSH_WRITE_REQUESTS); |
//assign o_wb_cyc = (wb_state == `WB_READ_REQUEST) |
//||(wb_state == `WB_WRITE_REQUEST) |
//||(wb_state == `WB_ACK) |
//||(wb_state == `WB_WAIT_ON_NEXT_WRITE); |
//assign o_wb_stb = (wb_state == `WB_READ_REQUEST) |
// ||(wb_state == `WB_WRITE_REQUEST); |
|
always @(posedge i_clk) |
if (i_rst) |
r_new_addr <= 1'b1; |
else if ((~o_wb_cyc)&&(i_stb)&&(~i_codword[35])) |
r_new_addr <= 1'b1; |
else if (o_wb_cyc) |
r_new_addr <= 1'b0; |
|
always @(posedge i_clk) |
if (~o_wb_cyc) |
r_acks_needed <= 10'h00; // (i_codword[35])?i_codword[9:0]:10'h00; |
else if ((o_wb_stb)&&(~i_wb_stall)&&(~i_wb_ack)) |
r_acks_needed <= r_acks_needed + 10'h01; |
else if (((~o_wb_stb)||(i_wb_stall))&&(i_wb_ack)) |
r_acks_needed <= r_acks_needed - 10'h01; |
|
always @(posedge i_clk) |
last_ack <= (~o_wb_stb)&&(r_acks_needed == 10'h01) |
||(o_wb_stb)&&(r_acks_needed == 10'h00); |
|
always @(posedge i_clk) |
zero_acks <= (~o_wb_stb)&&(r_acks_needed == 10'h00); |
|
always @(posedge i_clk) |
if (~o_wb_stb) // (~o_wb_cyc)&&(i_codword[35:34] == 2'b11)) |
r_len <= i_codword[9:0]; |
else if ((o_wb_stb)&&(~i_wb_stall)&&(|r_len)) |
r_len <= r_len - 10'h01; |
|
always @(posedge i_clk) |
begin |
single_read_request <= (~o_wb_cyc)&&(i_codword[9:0] == 10'h01); |
// When there is one read request left, it will be the last one |
// will be the last one |
last_read_request <= (o_wb_stb)&&(r_len[9:2] == 8'h00) |
&&((~r_len[1]) |
||((~r_len[0])&&(~i_wb_stall))); |
end |
|
/* |
reg [5:0] count; |
always @(posedge i_clk) |
if (~o_wb_cyc) |
count <= 0; |
else |
count <= count+1; |
assign o_dbg = (count > 6'd10); |
*/ |
// assign o_dbg = (wb_state == `WB_ACK); |
|
endmodule |
/rtl/wbudeword.v
0,0 → 1,96
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbudeword.v |
// |
// Project: FPGA library |
// |
// Purpose: Once a word has come from the bus, undergone compression, had |
// idle cycles and interrupts placed in it, this routine converts |
// that word form a 36-bit single word into a series of 6-bit words |
// that can head to the output routine. Hence, it 'deword's the value: |
// unencoding the 36-bit word encoding. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
module wbudeword(i_clk, i_stb, i_word, i_tx_busy, o_stb, o_nl_hexbits, o_busy); |
input i_clk, i_stb; |
input [35:0] i_word; |
input i_tx_busy; |
output reg o_stb; |
output reg [6:0] o_nl_hexbits; |
output reg o_busy; |
|
|
wire [2:0] w_len; |
assign w_len = (i_word[35:33]==3'b000)? 3'b001 |
: (i_word[35:32]==4'h2)? 3'b110 |
: (i_word[35:32]==4'h3)? (3'b010+{1'b0,i_word[31:30]}) |
: (i_word[35:34]==2'b01)? 3'b010 |
: (i_word[35:34]==2'b10)? 3'b001 |
: 3'b110; |
|
reg r_dly; |
reg [2:0] r_len; |
reg [29:0] r_word; |
initial o_stb = 1'b0; |
initial o_busy = 1'b0; |
initial r_dly = 1'b0; |
always @(posedge i_clk) |
if ((i_stb)&&(~o_busy)) // Only accept when not busy |
begin |
r_len <= w_len-3'b001; |
r_word <= i_word[29:0]; |
o_stb <= 1'b1; |
o_nl_hexbits <= { 1'b0, i_word[35:30] }; // No newline ... yet |
o_busy <= 1'b1; |
r_dly <= 1'b1; |
end else if ((o_stb)&&(i_tx_busy)) |
begin |
o_busy <= 1'b1; // wait and do nothing |
r_dly <= 1'b1; |
end else if (o_stb) // and (~i_tx_busy) means ours was accepted |
o_stb <= 1'b0; // Delay one clock |
else if (r_len > 0) |
begin |
o_stb <= 1'b1; |
o_nl_hexbits <= { 1'b0, r_word[29:24] }; |
r_word[29:6] <= r_word[23:0]; |
r_len <= r_len - 3'b001; |
o_busy <= 1'b1; // wait and do nothing |
r_dly <= 1'b1; |
end else if (~o_nl_hexbits[6]) |
begin |
o_stb <= 1'b1; |
o_nl_hexbits <= 7'h40; |
o_busy <= 1'b1; // wait and do nothing |
r_dly <= 1'b1; |
end else begin |
r_dly <= 1'b0; |
o_busy <= (r_dly); |
end |
|
endmodule |
|
/rtl/wbutohex.v
0,0 → 1,78
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbutohex.v |
// |
// Project: FPGA library |
// |
// Purpose: Supports a printable character conversion from a printable |
// ASCII character to six bits of valid data. The encoding is |
// as follows: |
// |
// 0-9 -> 0-9 |
// A-Z -> 10-35 |
// a-z -> 36-61 |
// @ -> 62 |
// % -> 63 |
// |
// Note that decoding is stateless, yet requires one clock. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 wbutohex(i_clk, i_stb, i_byte, o_stb, o_valid, o_hexbits); |
input i_clk, i_stb; |
input [7:0] i_byte; |
output reg o_stb, o_valid; |
output reg [5:0] o_hexbits; |
|
always @(posedge i_clk) |
o_stb <= i_stb; |
|
always @(posedge i_clk) |
begin |
// These are the defaults, to be overwridden by the ifs below |
o_valid <= 1'b1; |
o_hexbits <= 6'h00; |
|
if ((i_byte >= 8'h30)&&(i_byte <= 8'h39)) // A digit |
o_hexbits <= { 2'b0, i_byte[3:0] }; |
else if ((i_byte >= 8'h41)&&(i_byte <= 8'h5a)) // Upper case |
o_hexbits <= (i_byte[5:0] - 6'h01 + 6'h0a);// -'A'+10 |
else if ((i_byte >= 8'h61)&&(i_byte <= 8'h7a)) |
o_hexbits <= (i_byte[5:0] +6'h03); // -'a'+(10+26) |
else if (i_byte == 8'h40) // An '@' sign |
o_hexbits <= 6'h3e; |
else if (i_byte == 8'h25) // A '%' sign |
o_hexbits <= 6'h3f; |
else |
o_valid <= 1'b0; |
end |
endmodule |
|
/rtl/gpsclock.v
0,0 → 1,505
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: gpsclock.v |
// |
// Project: A GPS Schooled Clock Core |
// |
// Purpose: The purpose of this module is to school a counter, run off of |
// the FPGA's local oscillator, to match a GPS 1PPS signal. Should |
// the GPS 1PPS vanish, the result will flywheel with its last |
// solution (both frequency and phase) until GPS is available |
// again. |
// |
// This approach can be used to measure the speed of the |
// local oscillator, although there may be other more appropriate |
// means to do this. |
// |
// Note that this core does not produce anything more than |
// subsecond timing resolution. |
// |
// Parameters: This core needs two parameters set below, the DEFAULT_STEP |
// and the DEFAULT_WORD_STEP. The first must be set to |
// 2^RW / (nominal local clock rate), whereas the second must be |
// set to 2^(RW/2) / (nominal clock rate), where RW is the register |
// width used for our computations. (64 is sufficient for up to |
// 4 GHz clock speeds, 56 is minimum for 100 MHz.) Although |
// RW is listed as a variable parameter, I have no plans to |
// test values other than 64. So your mileage might vary there. |
// |
// Other parameters, alpha, beta, and gamma are specific to the |
// loop bandwidth you would like to choose. Please see the |
// accompanying specification for a selection of what values |
// may be useful. |
// |
// Inputs: |
// i_clk A synchronous clock signal for all logic. Must be slow enough |
// that the FPGA can accomplish 64 bit math. |
// |
// i_rst Resets the clock speed / counter step to be the nominal |
// value given by our parameter. This is useful in case the |
// control loop has gone off into never never land and doesn't |
// seem to be returning. |
// |
// i_pps The 1PPS signal from the GPS chip. |
// |
// Wishbone bus |
// |
// Outputs: |
// o_led No circuit would be complete without a properly blinking LED. |
// This one blinks an LED at the top of the GPS 1PPS and the |
// internal 1PPS. When the two match, the LED will be on for |
// 1/16th of a second. When no GPS 1PPS is present, the LED |
// will blink with a 50% duty cycle. |
// |
// o_tracking A boolean value indicating whether the control loop |
// is open (0) or closed (1). Does not indicate performance. |
// |
// o_count A counter, from zero to 2^RW-1, indicating the position |
// of the current clock within a second. (This'll be off by |
// two clocks due to internal latencies.) |
// |
// o_step The amount the counter, o_count, is stepped each clock. |
// This is related to the actual speed of the oscillator (when |
// locked) by f_XO = 2^(RW) / o_step. |
// |
// o_err For those interested in how well this device is performing, |
// this is the error signal coming out of the device. |
// |
// o_locked Indicates a locked condition. While it should work, |
// it isn't the best and most versatile lock indicator. A better |
// indicator should be based upon how good the user wants the |
// lock indicator to be. This isn't that. |
// |
// |
// 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 gpsclock(i_clk, i_rst, i_pps, o_pps, o_led, |
i_wb_cyc_stb, i_wb_we, i_wb_addr, i_wb_data, |
o_wb_ack, o_wb_stall, o_wb_data, |
o_tracking, o_count, o_step, o_err, o_locked, o_dbg); |
parameter RW=64, // Needs to be 2ceil(Log_2(i_clk frequency)) |
DW=32, // The width of our data bus |
ONE_SECOND = 0, |
NPW=RW-DW, // Width of non-parameter data |
HRW=RW/2; // Half of RW |
input i_clk, i_rst; |
input i_pps; // From the GPS device |
output reg o_pps; // To our local circuitry |
output reg o_led; // A blinky light showing how well we're doing |
// Wishbone Configuration interface |
input i_wb_cyc_stb, i_wb_we; |
input [1:0] i_wb_addr; |
input [(DW-1):0] i_wb_data; |
output reg o_wb_ack; |
output wire o_wb_stall; |
output reg [(DW-1):0] o_wb_data; |
// Status and timing outputs |
output reg o_tracking; // 1=closed loop, 0=open |
output reg [(RW-1):0] o_count, // Fraction of a second |
o_step, // 2^RW / clock speed (in Hz) |
o_err; // Fraction of a second err |
output reg o_locked; // 1 if Locked, 0 o.w. |
output wire [1:0] o_dbg; |
|
|
// Clock resynchronization variables |
reg pps_d, ck_pps, lst_pps; |
wire tick; // And a variable indicating the top of GPS 1PPS |
|
// |
// Configuration variables. These control the loop bandwidth, the speed |
// of convergence, the speed of adaptation to changes, and more. If |
// you adjust these outside of what the specification recommends, |
// be careful that the control loop still synchronizes! |
reg new_config; |
reg [5:0] r_alpha; |
reg [(DW-1):0] r_beta, r_gamma, r_def_step; |
reg [(RW-1):0] pre_step; |
|
// |
// This core really operates rather slowly, in FPGA time. Of the |
// millions of ticks per second, we only do things on about less than |
// a handful. These timing signals below help us to determine when |
// our data is valid during those handful. |
// |
// Timing |
reg err_tick, mpy_aux, mpy_sync_two, delay_step_clk; |
wire sub_tick, fltr_tick; |
|
// |
// When tracking, each second we'll produce a lowpass filtered_err |
// (via a recursive average), a count_correction and a step_correction. |
// The two _correction terms then get applied at the top of the second. |
// Here's the declaration of those parameters. The |
// 'pre_count_correction' parameter allows us to avoid adding three |
// 64-bit numbers in a single clock, splitting part of that amount into |
// an earlier clock. |
// |
// Tracking |
reg [(RW-1):0] count_correction, pre_count_correction; |
reg [(HRW-1):0] step_correction; |
reg [(HRW-1):0] delayed_step_correction, delayed_step; |
reg signed [(HRW-1):0] mpy_input; |
wire [(RW-1):0] w_mpy_out; |
wire signed [(RW-1):0] filter_sub_count, filtered_err; |
|
|
|
// |
// |
// |
// Wishbone access ... adjust our tracking parameters |
// |
// |
// |
// DEFAULT_STEP = 64'h0000_002a_f31d_c461, // 2^64 / 100 MHz |
initial r_def_step = 32'h8_2af_31dc; |
always @(posedge i_clk) |
pre_step <= { 16'h00, |
(({ r_def_step[27:0], 20'h00 })>>r_def_step[31:28])}; |
|
initial new_config = 1'b0; |
always @(posedge i_clk) |
if ((i_wb_cyc_stb)&&(i_wb_we)) |
begin |
new_config = 1'b1; |
case(i_wb_addr) |
2'b00: r_alpha <= i_wb_data[5:0]; |
2'b01: r_beta <= i_wb_data; |
2'b10: r_gamma <= i_wb_data; |
2'b11: r_def_step <= i_wb_data; |
default: begin end |
// r_defstep <= i_wb_data; |
endcase |
end else |
new_config = 1'b0; |
always @(posedge i_clk) |
case (i_wb_addr) |
2'b00: o_wb_data <= { 26'h00, r_alpha }; |
2'b01: o_wb_data <= r_beta; |
2'b10: o_wb_data <= r_gamma; |
2'b11: o_wb_data <= r_def_step; |
default: o_wb_data <= 0; |
endcase |
|
reg dly_config; |
initial dly_config = 1'b0; |
always @(posedge i_clk) |
dly_config <= new_config; |
always @(posedge i_clk) |
o_wb_ack <= i_wb_cyc_stb; |
assign o_wb_stall = 1'b0; |
|
|
// |
// |
// Deal with the realities of an unsynchronized 1PPS signal: |
// register it with two flip flops to avoid metastability issues. |
// Create a 'tick' variable to note the top of a second. |
// |
// |
always @(posedge i_clk) |
begin |
pps_d <= i_pps; |
ck_pps <= pps_d; |
lst_pps <= ck_pps; |
end |
|
// Provide a touch of debounce protection ... equal to about |
// one quarter of a second. |
reg [(RW-3):0] tick_enable_counter; |
wire [(RW-1):0] w_tick_enable_sum; |
wire w_tick_enable, w_tick_enable_unused; |
bigadd enabler(i_clk, 1'b0, o_step, { 2'b0, tick_enable_counter }, |
w_tick_enable_sum, w_tick_enable_unused); |
initial tick_enable_counter = 0; |
always @(posedge i_clk) |
begin |
if (tick) |
tick_enable_counter <= 0; |
else if (|w_tick_enable_sum[(RW-1):(RW-2)]) |
tick_enable_counter <= {(RW-2){1'b1}}; |
else |
tick_enable_counter <= w_tick_enable_sum[(RW-3):0]; |
end |
assign w_tick_enable = tick_enable_counter[(RW-3)]; |
|
assign tick= (ck_pps)&&(~lst_pps)&&(w_tick_enable); |
assign o_dbg[0] = tick; |
assign o_dbg[1] = w_tick_enable; |
|
// |
// |
// Here's our counter proper: Add o_step to o_count each clock tick |
// to have a current time value. Corrections are applied at the top |
// of the second if we are in tracking mode. The 'o_pps' signal is |
// generated from the carry/overflow of the o_count addition. |
// |
// |
reg cnt_carry; |
reg [31:0] p_count; |
initial o_count = 0; |
initial o_pps = 1'b0; |
always @(posedge i_clk) |
if ((o_tracking)&&(tick)) |
begin |
{ cnt_carry, p_count } <= p_count[31:0] + count_correction[31:0]; |
if (~count_correction[(RW-1)]) |
begin |
// Note that we don't create an o_pps just |
// because the gps_pps states that there should |
// be one. Instead, we hold to the normal |
// means of business. At the tick, however, |
// we add both the step and the correction to |
// the current count. |
{ o_pps, o_count[63:32] } <= o_count[63:32] +count_correction[63:32]+ { 31'h00, cnt_carry }; |
end else begin |
// If the count correction is negative, it means |
// we need to go backwards. In this case, |
// there shouldn't be any o_pps, least we get |
// two of them. |
o_pps <= 1'b0; |
o_count[63:32] <= o_count[63:32] + count_correction[63:32]; |
end |
end else begin |
// The difference between count_correction and |
// o_step is the phase correction from the last tick. |
// If we aren't tracking, we don't want to use the |
// correction. Likewise, even if we are, we only |
// want to use it on the ticks. |
{ cnt_carry, p_count } <= p_count + o_step[31:0]; |
{ o_pps, o_count[63:32] } <= o_count[63:32] + o_step[63:32]; |
end |
always @(posedge i_clk) |
o_count[31:0] <= p_count; |
|
reg [(HRW):0] step_correction_plus_carry; |
always @(posedge i_clk) |
step_correction_plus_carry = step_correction + { 31'h00, delayed_carry }; |
|
wire w_step_correct_unused; |
wire [(RW-1):0] new_step; |
bigadd getnewstep(i_clk, 1'b0, o_step, |
{ { (HRW-1){step_correction_plus_carry[HRW]} }, |
step_correction_plus_carry}, |
new_step, w_step_correct_unused); |
|
reg delayed_carry; |
initial delayed_carry = 0; |
initial o_step = 64'h002af31dc461; |
always @(posedge i_clk) |
if ((i_rst)||(dly_config)) |
o_step <= pre_step; |
else if ((o_tracking) && (tick)) |
o_step <= new_step; |
|
initial delayed_step = 0; |
always @(posedge i_clk) |
if ((i_rst)||(dly_config)) |
delayed_step <= 0; |
else if (delay_step_clk) |
{ delayed_carry, delayed_step } <= delayed_step |
+ delayed_step_correction; |
|
|
|
// |
// |
// Now to start our tracking loop. The steps are: |
// 1. Measure our error |
// 2. Filter our error (lowpass, recursive averager) |
// 3. Multiply the filtered error by two user-supplied constants |
// (beta and gamma) |
// 4. The results of this multiply then become the new |
// count and step corrections. |
// |
// |
// A negative error means we were too fast ... the count rolled over |
// and is near zero, the o_err is then the negation of this when the |
// tick does show up. |
// |
initial o_err = 0; |
always @(posedge i_clk) |
if (tick) |
o_err <= ONE_SECOND - o_count; |
|
initial err_tick = 1'b0; |
always @(posedge i_clk) |
err_tick <= tick; |
|
bigsub suberri(i_clk, err_tick, o_err, |
filtered_err, filter_sub_count, sub_tick); |
|
// |
// This shouldn't be required: We only want to shift our |
// filter_sub_count by r_alpha bits, why the extra struggles? |
// Why is because Verilator decides that these values are unsigned, |
// and so despite being told that they are signed values, verilator |
// doesn't sign extend them upon shifting. Put together, |
// { shift_hi[low-bits], shift_lo[low-bits] } make up a full RW |
// bit correction factor. |
reg signed [(RW-1):0] shift_hi, shift_lo; |
always @(posedge i_clk) |
begin |
shift_hi <= { {(HRW){filter_sub_count[(RW-1)]}}, |
filter_sub_count[(RW-1):HRW] }>>r_alpha; |
shift_lo <= filter_sub_count[(RW-1):0]>>r_alpha; |
end |
|
bigadd adderr(i_clk, sub_tick, filtered_err, |
{ shift_hi[(HRW-1):0], shift_lo[(HRW-1):0] }, |
filtered_err, fltr_tick); |
/* |
always @(posedge i_clk) |
if ((o_tracking)&&(sub_tick)) |
filtered_err<= filtered_err |
+ { shift_hi[(HRW-1):0], shift_lo[(HRW-1):0] }; |
*/ |
|
always @(posedge i_clk) |
if (fltr_tick) |
mpy_input <= r_beta; |
else |
mpy_input <= r_gamma; |
always @(posedge i_clk) |
mpy_aux <= fltr_tick; |
|
// |
// The multiply |
// |
wire mpy_sync; |
wire [(RW-1):0] mpy_out; |
initial mpy_sync_two = 1'b0; |
// Sign extend all inputs to RW bits |
wire signed [(RW-1):0] w_mpy_input, w_mpy_err; |
assign w_mpy_input = { {(RW-DW){mpy_input[(DW-1)]}}, mpy_input[(DW-1):0]}; |
assign w_mpy_err = { {(RW-NPW){filtered_err[(RW-1)]}}, filtered_err[(RW-1):(RW-NPW)]}; |
bigsmpy mpyi(i_clk, mpy_aux, 1'b1, w_mpy_input[31:0], w_mpy_err[31:0], |
mpy_out, mpy_sync); |
always @(posedge i_clk) |
mpy_sync_two <= mpy_sync; |
assign w_mpy_out = mpy_out; |
|
// The post-multiply |
initial pre_count_correction = 0; |
initial step_correction = 0; |
initial delayed_step_correction = 0; |
always @(posedge i_clk) |
if (mpy_sync) |
pre_count_correction <= w_mpy_out; |
else if (mpy_sync_two) begin |
step_correction <= w_mpy_out[(RW-1):HRW]; |
delayed_step_correction <= w_mpy_out[(HRW-1):0]; |
end |
always @(posedge i_clk) |
count_correction <= pre_count_correction + o_step; |
|
initial delay_step_clk = 1'b0; |
always @(posedge i_clk) |
delay_step_clk <= mpy_sync_two; |
|
// |
// |
// LED Logic -- Note that this is where we tell if we've had a GPS |
// 1PPS pulse or not. To have had such a pulse, it needs to have |
// been within the last two seconds. |
// |
// |
reg no_pulse; |
reg [32:0] time_since_pps; |
initial no_pulse = 1'b1; |
initial time_since_pps = 33'hffffffff; |
always @(posedge i_clk) |
if (tick) |
begin |
time_since_pps <= 0; |
no_pulse <= 0; |
end else if (time_since_pps[32:29] == 4'hf) |
begin |
time_since_pps <= 33'hffffffff; |
no_pulse <= 1'b1; |
end else |
time_since_pps <= time_since_pps + pre_step[(RW-1):HRW]; |
|
// |
// 1. Pulse with a 50% duty cycle every second if no GPS is available. |
// 2. Pulse with a 6% duty cycle any time a pulse is present, and any |
// time we think (when a pulse is present) that we have time. |
// |
// This should produce a set of conflicting pulses when out of lock, |
// and a nice short once per second pulse when locked. Further, you |
// should be able to tell when the core is flywheeling by the duration |
// of the pulses (50% vs 6%). |
// |
always @(posedge i_clk) |
if (no_pulse) |
o_led <= o_count[(RW-1)]; |
else |
o_led <= ((time_since_pps[31:28] == 4'h0) |
||(o_count[(RW-1):(RW-4)]== 4'h0)); |
|
// |
// |
// Now, are we tracking or not? |
// We'll attempt to close the loop after seeing 7 valid GPS 1PPS |
// rising edges. |
// |
// |
reg [2:0] count_valid_ticks; |
initial count_valid_ticks = 3'h0; |
always @(posedge i_clk) |
if ((tick)&&(count_valid_ticks < 3'h7)) |
count_valid_ticks <= count_valid_ticks+1; |
else if (no_pulse) |
count_valid_ticks <= 3'h0; |
initial o_tracking = 1'b0; |
always @(posedge i_clk) |
if ((tick)&&(&count_valid_ticks)) |
o_tracking <= 1'b1; |
else if ((tick)||(count_valid_ticks == 0)) |
o_tracking <= 1'b0; |
|
// |
// |
// Are we locked or not? |
// We'll use the top eight bits of our error to tell. If the top eight |
// bits are all ones or all zeros, then we'll call ourselves locked. |
// This is equivalent to calling ourselves locked if, at the top of |
// the second, we are within 1/128th of a second of the GPS 1PPS. |
// |
initial o_locked = 1'b0; |
always @(posedge i_clk) |
if ((o_tracking)&&(tick)&&( |
(( o_err[(RW-1)])&&(o_err[(RW-1):(RW-8)]==8'hff)) |
||((~o_err[(RW-1)])&&(o_err[(RW-1):(RW-8)]==8'h00)))) |
o_locked <= 1'b1; |
else if (tick) |
o_locked <= 1'b0; |
|
endmodule |
|
/rtl/wbubus.v
0,0 → 1,138
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbubus.v |
// |
// Project: FPGA library |
// |
// Purpose: This is the top level file for the entire JTAG-USB to Wishbone |
// bus conversion. (It's also the place to start debugging, should |
// things not go as planned.) Bytes come into this routine, bytes go out, |
// and the wishbone bus (external to this routine) is commanded in between. |
// |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
// |
module wbubus(i_clk, i_rx_stb, i_rx_data, |
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, |
i_interrupt, |
o_tx_stb, o_tx_data, i_tx_busy); |
parameter LGWATCHDOG=19; |
input i_clk; |
input i_rx_stb; |
input [7:0] i_rx_data; |
output wire o_wb_cyc, o_wb_stb, o_wb_we; |
output wire [31:0] o_wb_addr, o_wb_data; |
input i_wb_ack, i_wb_stall, i_wb_err; |
input [31:0] i_wb_data; |
input i_interrupt; |
output wire o_tx_stb; |
output wire [7:0] o_tx_data; |
input i_tx_busy; |
// output wire o_dbg; |
|
|
reg r_wdt_reset; |
|
// Decode ASCII input requests into WB bus cycle requests |
wire in_stb; |
wire [35:0] in_word; |
wbuinput getinput(i_clk, i_rx_stb, i_rx_data, in_stb, in_word); |
|
wire w_bus_busy, fifo_in_stb, exec_stb, w_bus_reset; |
wire [35:0] fifo_in_word, exec_word; |
// `define NO_INPUT_FIFO |
`ifdef NO_INPUT_FIFO |
assign fifo_in_stb = in_stb; |
assign fifo_in_word = in_word; |
assign w_bus_reset = 1'b0; |
`else |
wire ififo_empty_n, ififo_err; |
assign fifo_in_stb = (~w_bus_busy)&&(ififo_empty_n); |
assign w_bus_reset = r_wdt_reset; |
wbufifo #(36,6) padififo(i_clk, w_bus_reset, |
in_stb, in_word, fifo_in_stb, fifo_in_word, |
ififo_empty_n, ififo_err); |
`endif |
|
// assign o_dbg = (i_wb_ack)&&(i_wb_cyc); |
|
// Take requests in, Run the bus, send results out |
// This only works if no requests come in while requests |
// are pending. |
wbuexec runwb(i_clk, r_wdt_reset, fifo_in_stb, fifo_in_word, w_bus_busy, |
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, |
exec_stb, exec_word); |
|
/* |
wire [31:0] cyc_debug; |
assign cyc_debug = { 1'b0, o_wb_cyc, o_wb_stb, o_wb_we, i_wb_ack, i_wb_stall, |
(i_wb_err||r_wdt_reset), o_wb_addr[14:0], |
o_wb_data[4:0], i_wb_data[4:0] }; |
assign o_dbg = cyc_debug; |
*/ |
/* |
wire [31:0] fif_debug; |
assign fif_debug = { |
(exec_stb)&&(exec_word[35:30] == 6'h05),// 1 |
fifo_in_stb, fifo_in_word[35:30], // 7 |
exec_stb, exec_word[35:30], // 7 |
o_wb_cyc, o_wb_stb, o_wb_we, |
i_wb_ack, i_wb_stall, // 5 |
w_bus_busy, ififo_empty_n, w_bus_reset, // 3 |
i_rx_stb, o_wb_addr[7:0] }; // 9 |
assign o_dbg = fif_debug; |
*/ |
|
wire ofifo_err; |
// wire [30:0] out_dbg; |
wbuoutput wroutput(i_clk, w_bus_reset, |
exec_stb, exec_word, |
o_wb_cyc, i_interrupt, exec_stb, |
o_tx_stb, o_tx_data, i_tx_busy, ofifo_err); |
|
// Add in a watchdog timer to the bus |
reg [(LGWATCHDOG-1):0] r_wdt_timer; |
initial r_wdt_reset = 1'b0; |
initial r_wdt_timer = 0; |
always @(posedge i_clk) |
if ((~o_wb_cyc)||(i_wb_ack)) |
begin |
r_wdt_timer <= 0; |
r_wdt_reset <= 1'b0; |
end else if (&r_wdt_timer) |
begin |
r_wdt_reset <= 1'b1; |
r_wdt_timer <= 0; |
end else begin |
r_wdt_timer <= r_wdt_timer+{{(LGWATCHDOG-1){1'b0}},1'b1}; |
r_wdt_reset <= 1'b0; |
end |
|
endmodule |
|
/rtl/Makefile
0,0 → 1,96
##########################################################################/ |
## |
## Filename: Makefile |
## |
## Project: OpenArty, an entirely open SoC based upon the Arty platform |
## |
## Purpose: To direct the Verilator build of the SoC sources. The result |
## is C++ code (built by Verilator), that is then built (herein) |
## into a library. |
## |
## |
## 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. |
## |
## License: GPL, v3, as defined and found on www.gnu.org, |
## http:##www.gnu.org/licenses/gpl.html |
## |
## |
##########################################################################/ |
## |
## |
all: test |
YYMMDD=`date +%Y%m%d` |
CXX := g++ |
FBDIR := . |
VDIRFB:= $(FBDIR)/obj_dir |
|
.PHONY: test |
test: $(VDIRFB)/Veqspiflash__ALL.a |
test: $(VDIRFB)/Venetctrl__ALL.a |
test: $(VDIRFB)/Vfastmaster__ALL.a |
# test: $(VDIRFB)/Vfastmaster__ALL.a |
|
CPUDR := cpu |
CPUSOURCESnD := zipcpu.v fastops.v pfcache.v pipemem.v \ |
pfcache.v ifastdec.v wbpriarbiter.v zipbones.v \ |
zipsystem.v zipcounter.v zipjiffies.v ziptimer.v \ |
wbdmac.v icontrol.v wbwatchdog.v |
CPUSOURCES := $(addprefix $(CPUDR)/,$(CPUSOURCESnD)) |
|
JTAGBUS := wbufifo.v wbubus.v wbucompactlines.v \ |
wbucompress.v wbudecompress.v wbudeword.v wbuexec.v \ |
wbuidleint.v wbuinput.v wbuoutput.v wbureadcw.v wbusixchar.v \ |
wbutohex.v |
PERIPHERALS:= enetctrl.v fastio.v icontrol.v rtcdate.v rtcgps.v \ |
rxuart.v txuart.v eqspiflash.v llqspi.v flash_config.v \ |
wbicapetwo.v sdspi.v gpsclock_tb.v gpsclock.v wboled.v lloled.v \ |
wbscopc.v wbscope.v memdev.v wbddrsdram.v |
BIGMATH:= bigadd.v bigsmpy.v bigsub.v |
SOURCES := fastmaster.v builddate.v \ |
$(CPUSOURCES) $(JTAGBUS) $(PERIPHERALS) $(BIGMATH) |
SLOWSRC := busmaster.v builddate.v \ |
$(CPUSOURCES) $(JTAGBUS) $(PERIPHERALS) $(BIGMATH) |
|
$(VDIRFB)/Vfastmaster__ALL.a: $(VDIRFB)/Vfastmaster.h $(VDIRFB)/Vfastmaster.cpp |
$(VDIRFB)/Vfastmaster__ALL.a: $(VDIRFB)/Vfastmaster.mk |
$(VDIRFB)/Vfastmaster.h $(VDIRFB)/Vfastmaster.cpp $(VDIRFB)/Vfastmaster.mk: $(SOURCES) |
|
$(VDIRFB)/Vbusmaster__ALL.a: $(VDIRFB)/Vbusmaster.h $(VDIRFB)/Vbusmaster.cpp |
$(VDIRFB)/Vbusmaster__ALL.a: $(VDIRFB)/Vbusmaster.mk |
$(VDIRFB)/Vbusmaster.h $(VDIRFB)/Vbusmaster.cpp $(VDIRFB)/Vbusmaster.mk: $(SLOWSRC) |
|
$(VDIRFB)/Venetctrl.h $(VDIRFB)/Venetctrl.cpp $(VDIRFB)/Venetctrl.mk: enetctrl.v |
$(VDIRFB)/Veqspiflash.h $(VDIRFB)/Veqspiflash.cpp $(VDIRFB)/Veqspiflash.mk: eqspiflash.v llqspi.v |
$(VDIRFB)/V%.cpp $(VDIRFB)/V%.h $(VDIRFB)/V%.mk: $(FBDIR)/%.v |
verilator -cc -y $(CPUDR) $*.v |
|
|
$(VDIRFB)/V%__ALL.a: $(VDIRFB)/V%.mk |
cd $(VDIRFB); make -f V$*.mk |
|
.PHONY: |
archive: |
tar --transform s,^,$(YYMMDD)-rtl/, -chjf $(YYMMDD)-rtl.tjz Makefile *.v cpu/*.v |
|
.PHONY: clean |
clean: |
rm -rf $(VDIRFB)/*.mk |
rm -rf $(VDIRFB)/*.cpp |
rm -rf $(VDIRFB)/*.h |
rm -rf $(VDIRFB)/ |
|
/rtl/fastmaster.v
0,0 → 1,1076
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: fastmaster.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: On other projects, this file would be called the "bus |
// interconnect". This module connects all the devices on the |
// Wishbone bus within this project together. It is created by hand, not |
// automatically. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`define NO_ZIP_WBU_DELAY |
// `define ZIPCPU |
`ifdef ZIPCPU |
//`define ZIP_SYSTEM |
`ifndef ZIP_SYSTEM |
`define ZIP_BONES |
`endif // ZIP_SYSTEM |
`endif // ZipCPU |
// |
// |
`define SDCARD_ACCESS |
`define ETHERNET_ACCESS |
`ifndef VERILATOR |
`define ICAPE_ACCESS |
`endif |
`define FLASH_ACCESS |
//`define SDRAM_ACCESS |
`define GPS_CLOCK |
// UART_ACCESS and GPS_UART have both been placed within fastio |
// `define UART_ACCESS |
// `define GPS_UART |
`define RTC_ACCESS |
`define OLEDRGB_ACCESS |
// |
// `define CPU_SCOPE |
// `define GPS_SCOPE |
`define FLASH_SCOPE |
// `define SDRAM_SCOPE |
// `define ENET_SCOPE |
// |
// |
module fastmaster(i_clk, i_rst, |
// CNC |
i_rx_stb, i_rx_data, o_tx_stb, o_tx_data, i_tx_busy, |
// Boad I/O |
i_sw, i_btn, o_led, |
o_clr_led0, o_clr_led1, o_clr_led2, o_clr_led3, |
// PMod I/O |
i_aux_rx, o_aux_tx, o_aux_cts, i_gps_rx, o_gps_tx, |
// The Quad SPI Flash |
o_qspi_cs_n, o_qspi_sck, o_qspi_dat, i_qspi_dat, o_qspi_mod, |
// The DDR3 SDRAM |
o_ddr_reset_n, o_ddr_cke, |
o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n, |
o_ddr_dqs, o_ddr_addr, o_ddr_ba, o_ddr_data, i_ddr_data, |
// The SD Card |
o_sd_sck, o_sd_cmd, o_sd_data, i_sd_cmd, i_sd_data, i_sd_detect, |
// Ethernet control (MDIO) lines |
o_mdclk, o_mdio, o_mdwe, i_mdio, |
// OLED Control interface (roughly SPI) |
o_oled_sck, o_oled_cs_n, o_oled_mosi, o_oled_dcn, |
o_oled_reset_n, o_oled_vccen, o_oled_pmoden, |
// The GPS PMod |
i_gps_pps, i_gps_3df |
); |
parameter ZA=24, ZIPINTS=13; |
input i_clk, i_rst; |
// The bus commander, via an external uart port |
input i_rx_stb; |
input [7:0] i_rx_data; |
output wire o_tx_stb; |
output wire [7:0] o_tx_data; |
input i_tx_busy; |
// I/O to/from board level devices |
input [3:0] i_sw; // 16 switch bus |
input [3:0] i_btn; // 5 Buttons |
output wire [3:0] o_led; // 16 wide LED's |
output wire [2:0] o_clr_led0, o_clr_led1, o_clr_led2, o_clr_led3; |
// PMod UARTs |
input i_aux_rx; |
output wire o_aux_tx, o_aux_cts; |
input i_gps_rx; |
output wire o_gps_tx; |
// Quad-SPI flash control |
output wire o_qspi_cs_n, o_qspi_sck; |
output wire [3:0] o_qspi_dat; |
input [3:0] i_qspi_dat; |
output wire [1:0] o_qspi_mod; |
// DDR3 RAM controller |
output wire o_ddr_reset_n, o_ddr_cke, |
o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n,o_ddr_we_n; |
output wire [2:0] o_ddr_dqs; |
output wire [13:0] o_ddr_addr; |
output wire [2:0] o_ddr_ba; |
output wire [31:0] o_ddr_data; |
input [31:0] i_ddr_data; |
// The SD Card |
output wire o_sd_sck; |
output wire o_sd_cmd; |
output wire [3:0] o_sd_data; |
input i_sd_cmd; |
input [3:0] i_sd_data; |
input i_sd_detect; |
// Ethernet control (MDIO) |
output wire o_mdclk, o_mdio, o_mdwe; |
input i_mdio; |
// OLEDRGB interface |
output wire o_oled_sck, o_oled_cs_n, o_oled_mosi, |
o_oled_dcn, o_oled_reset_n, o_oled_vccen, |
o_oled_pmoden; |
// GPS PMod (GPS UART above) |
input i_gps_pps; |
input i_gps_3df; |
|
// |
// |
// Master wishbone wires |
// |
// |
wire wb_cyc, wb_stb, wb_we, wb_stall, wb_err; |
wire [31:0] wb_data, wb_addr; |
reg wb_ack; |
reg [31:0] wb_idata; |
|
// Interrupts |
wire gpio_int, oled_int, flash_int, scop_int; |
wire enet_tx_int, enet_rx_int, sdcard_int, rtc_int, rtc_pps, |
auxrx_int, auxtx_int, gpsrx_int, sw_int, btn_int; |
|
// |
// |
// First BUS master source: The UART |
// |
// |
wire [31:0] dwb_idata; |
|
// Wires going to devices |
wire wbu_cyc, wbu_stb, wbu_we; |
wire [31:0] wbu_addr, wbu_data; |
// and then coming from devices |
wire wbu_ack, wbu_stall, wbu_err; |
wire [31:0] wbu_idata; |
// And then headed back home |
wire w_interrupt; |
// Oh, and the debug control for the ZIP CPU |
wire wbu_zip_sel, zip_dbg_ack, zip_dbg_stall; |
wire [31:0] zip_dbg_data; |
wbubus genbus(i_clk, i_rx_stb, i_rx_data, |
wbu_cyc, wbu_stb, wbu_we, wbu_addr, wbu_data, |
(wbu_zip_sel)?zip_dbg_ack:wbu_ack, |
(wbu_zip_sel)?zip_dbg_stall:wbu_stall, |
wbu_err, |
(wbu_zip_sel)?zip_dbg_data:wbu_idata, |
w_interrupt, |
o_tx_stb, o_tx_data, i_tx_busy); |
|
// assign o_dbg = (wbu_ack)&&(wbu_cyc); |
|
wire zip_cpu_int; // True if the CPU suddenly halts |
`ifdef ZIPCPU |
// Are we trying to access the ZipCPU? Such accesses must be special, |
// because they must succeed regardless of whether or not the ZipCPU |
// is on the bus. Hence, we trap them here. |
assign wbu_zip_sel = (wbu_addr[27]); |
|
// |
// |
// Second BUS master source: The ZipCPU |
// |
// |
wire zip_cyc, zip_stb, zip_we; |
wire [(ZA-1):0] w_zip_addr; |
wire [31:0] zip_data, zip_scope_data; |
// and then coming from devices |
wire zip_ack, zip_stall, zip_err; |
|
`ifdef ZIP_SYSTEM |
wire [(ZIPINTS-1):0] zip_interrupt_vec = { |
// Lazy(ier) interrupts |
oled_int, gpio_int, rtc_int, scop_int, flash_int, sw_int, btn_int, |
// Fast interrupts |
sdcard_int, auxtx_int, auxrx_int, enet_tx_int, enet_rx_int, |
gpsrx_int, rtc_pps |
}; |
|
zipsystem #( .RESET_ADDRESS(24'h08000), |
.ADDRESS_WIDTH(ZA), |
.LGICACHE(10), |
.START_HALTED(1), |
.EXTERNAL_INTERRUPTS(ZIPINTS), |
.HIGHSPEED_CPU(1)) |
zippy(i_clk, i_rst, |
// Zippys wishbone interface |
zip_cyc, zip_stb, zip_we, w_zip_addr, zip_data, |
zip_ack, zip_stall, dwb_idata, zip_err, |
zip_interrupt_vec, zip_cpu_int, |
// Debug wishbone interface |
((wbu_cyc)&&(wbu_zip_sel)), |
((wbu_stb)&&(wbu_zip_sel)),wbu_we, wbu_addr[0], |
wbu_data, |
zip_dbg_ack, zip_dbg_stall, zip_dbg_data |
`ifdef CPU_DEBUG |
, zip_scope_data |
`endif |
); |
`else // ZIP_SYSTEM |
wire w_zip_cpu_int_ignored; |
zipbones #( .RESET_ADDRESS(24'h08000), |
.ADDRESS_WIDTH(ZA), |
.LGICACHE(10), |
.START_HALTED(1), |
.HIGHSPEED_CPU(1)) |
zippy(i_clk, i_rst, |
// Zippys wishbone interface |
zip_cyc, zip_stb, zip_we, w_zip_addr, zip_data, |
zip_ack, zip_stall, dwb_idata, zip_err, |
w_interrupt, w_zip_cpu_int_ignored, |
// Debug wishbone interface |
((wbu_cyc)&&(wbu_zip_sel)), |
((wbu_stb)&&(wbu_zip_sel)),wbu_we, wbu_addr[0], |
wbu_data, |
zip_dbg_ack, zip_dbg_stall, zip_dbg_data |
`ifdef CPU_DEBUG |
, zip_scope_data |
`endif |
); |
assign zip_cpu_int = 1'b0; |
`endif // ZIP_SYSTEM v ZIP_BONES |
|
wire [31:0] zip_addr; |
generate |
if (ZA < 32) |
assign zip_addr = { {(32-ZA){1'b0}}, w_zip_addr}; |
else |
assign zip_addr = w_zip_addr; |
endgenerate |
|
// |
// |
// And an arbiter to decide who gets to access the bus |
// |
// |
wire dwb_we, dwb_stb, dwb_cyc, dwb_ack, dwb_stall, dwb_err; |
wire [31:0] dwb_addr, dwb_odata; |
wbpriarbiter #(32,32) wbu_zip_arbiter(i_clk, |
// The ZIP CPU Master -- Gets the priority slot |
zip_cyc, zip_stb, zip_we, zip_addr, zip_data, |
zip_ack, zip_stall, zip_err, |
// The UART interface Master |
(wbu_cyc)&&(~wbu_zip_sel), (wbu_stb)&&(~wbu_zip_sel), wbu_we, |
wbu_addr, wbu_data, |
wbu_ack, wbu_stall, wbu_err, |
// Common bus returns |
dwb_cyc, dwb_stb, dwb_we, dwb_addr, dwb_odata, |
dwb_ack, dwb_stall, dwb_err); |
|
// |
// |
// And because the ZIP CPU and the Arbiter create an unacceptable |
// delay, we fail timing. So we add in a delay cycle ... |
// |
// |
assign wbu_idata = dwb_idata; |
busdelay wbu_zip_delay(i_clk, |
dwb_cyc, dwb_stb, dwb_we, dwb_addr, dwb_odata, |
dwb_ack, dwb_stall, dwb_idata, dwb_err, |
wb_cyc, wb_stb, wb_we, wb_addr, wb_data, |
wb_ack, wb_stall, wb_idata, wb_err); |
|
`else // ZIPCPU |
assign zip_cpu_int = 1'b0; // No CPU here to halt |
assign wbu_zip_sel = 1'b0; |
|
// If there's no ZipCPU, there's no need for a Zip/WB-Uart bus delay. |
// We can go directly from the WB-Uart master bus to the master bus |
// itself. |
assign wb_cyc = wbu_cyc; |
assign wb_stb = wbu_stb; |
assign wb_we = wbu_we; |
assign wb_addr = wbu_addr; |
assign wb_data = wbu_data; |
assign wbu_idata = wb_idata; |
assign wbu_ack = wb_ack; |
assign wbu_stall = wb_stall; |
assign wbu_err = wb_err; |
|
// The CPU never halts if it doesn't exist, so set this interrupt to |
// zero. |
assign zip_cpu_int= 1'b0; |
`endif // ZIPCPU |
|
|
// |
// Peripheral select lines. |
// |
// These lines will be true during any wishbone cycle whose address |
// line selects the given I/O peripheral. The none_sel and many_sel |
// lines are used to detect problems, such as when no device is |
// selected or many devices are selected. Such problems will lead to |
// bus errors (below). |
// |
wire io_sel, scop_sel, netb_sel, |
flctl_sel, rtc_sel, sdcard_sel, netp_sel, |
oled_sel, gps_sel, mio_sel, cfg_sel, |
mem_sel, flash_sel, ram_sel, |
none_sel, many_sel; |
|
wire [4:0] skipaddr; |
assign skipaddr = { wb_addr[26], wb_addr[22], wb_addr[15], wb_addr[11], |
~wb_addr[8] }; |
assign ram_sel = (skipaddr[4]); |
assign flash_sel = (skipaddr[4:3]==2'b01); |
assign mem_sel = (skipaddr[4:2]==3'b001); |
assign netb_sel = (skipaddr[4:1]==4'b0001); |
assign io_sel = (~|skipaddr)&&(wb_addr[7:5]==3'b000); |
assign scop_sel = (~|skipaddr)&&(wb_addr[7:3]==5'b00100); |
assign rtc_sel = (~|skipaddr)&&(wb_addr[7:2]==6'b001010); |
assign sdcard_sel= (~|skipaddr)&&(wb_addr[7:2]==6'b001011); |
assign netp_sel = (~|skipaddr)&&(wb_addr[7:2]==6'b001101); |
assign oled_sel = (~|skipaddr)&&(wb_addr[7:2]==6'b001110); |
assign gps_sel = (~|skipaddr)&&( (wb_addr[7:2]==6'b001100) |
|| (wb_addr[7:3]==5'b01000)); |
assign mio_sel = (~|skipaddr)&&(wb_addr[7:5]==3'b101); |
assign flctl_sel = (~|skipaddr)&&(wb_addr[7:5]==3'b110); |
assign cfg_sel = (~|skipaddr)&&(wb_addr[7:5]==3'b111); |
|
wire skiperr; |
assign skiperr = (|wb_addr[31:27]) |
||(~skipaddr[4])&&(|wb_addr[25:23]) |
||(skipaddr[4:3]==2'b00)&&(|wb_addr[21:16]) |
||(skipaddr[4:2]==3'b000)&&(|wb_addr[14:12]) |
||(skipaddr[4:1]==4'b0000)&&(|wb_addr[10:9]); |
|
|
// |
// Peripheral acknowledgement lines |
// |
// These are only a touch more confusing, since the flash device will |
// ACK for both flctl_sel (the control line select), as well as the |
// flash_sel (the memory line select). Hence we have one fewer ack |
// line. |
wire io_ack, oled_ack, |
rtc_ack, sdcard_ack, |
netp_ack, gps_ack, mio_ack, cfg_ack, netb_ack, |
mem_ack, flash_ack, ram_ack; |
reg many_ack, slow_many_ack; |
reg slow_ack, scop_ack; |
wire [4:0] ack_list; |
assign ack_list = { ram_ack, flash_ack, mem_ack, netb_ack, cfg_ack }; |
initial many_ack = 1'b0; |
always @(posedge i_clk) |
many_ack <= ((ack_list != 5'h10) |
&&(ack_list != 5'h8) |
&&(ack_list != 5'h4) |
&&(ack_list != 5'h2) |
&&(ack_list != 5'h1) |
&&(ack_list != 5'h0)); |
/* |
assign many_ack = ( { 2'h0, ram_ack} |
+{2'h0, flash_ack } |
+{2'h0, mem_ack } |
+{2'h0, netb_ack } |
+{2'h0, slow_ack } > 3'h1 ); |
*/ |
|
wire [7:0] slow_ack_list; |
assign slow_ack_list = { mio_ack, gps_ack, netp_ack, |
sdcard_ack, rtc_ack, scop_ack, oled_ack, io_ack }; |
initial slow_many_ack = 1'b0; |
always @(posedge i_clk) |
slow_many_ack <= ((slow_ack_list != 8'h80) |
&&(slow_ack_list != 8'h40) |
&&(slow_ack_list != 8'h20) |
&&(slow_ack_list != 8'h10) |
&&(slow_ack_list != 8'h08) |
&&(slow_ack_list != 8'h04) |
&&(slow_ack_list != 8'h02) |
&&(slow_ack_list != 8'h01) |
&&(slow_ack_list != 8'h00)); |
|
always @(posedge i_clk) |
wb_ack <= (wb_cyc)&&(|{ ram_ack, flash_ack, mem_ack, |
netb_ack, cfg_ack, slow_ack }); |
always @(posedge i_clk) |
slow_ack <= (wb_cyc)&&(|{oled_ack, mio_ack, gps_ack, |
netp_ack, sdcard_ack, rtc_ack, scop_ack, |
oled_ack, io_ack}); |
|
// |
// Peripheral data lines |
// |
wire [31:0] io_data, oled_data, |
rtc_data, sdcard_data, |
netp_data, gps_data, mio_data, cfg_data, netb_data, |
mem_data, flash_data, ram_data; |
reg [31:0] slow_data, scop_data; |
|
// 4 control lines, 5x32 data lines ... |
always @(posedge i_clk) |
if ((ram_ack)||(flash_ack)) |
wb_idata <= (ram_ack)?ram_data:flash_data; |
else if ((mem_ack)||(netb_ack)) |
wb_idata <= (mem_ack)?mem_data:netb_data; |
else |
wb_idata <= slow_data; |
|
// 7 control lines, 8x32 data lines |
always @(posedge i_clk) |
if ((cfg_ack)||(mio_ack)) |
slow_data <= (cfg_ack) ? cfg_data : mio_data; |
else if ((gps_ack)||(netp_ack)) |
slow_data <= (gps_ack) ? gps_data : netp_data; |
else if ((sdcard_ack)||(rtc_ack)) |
slow_data <= (sdcard_ack)?sdcard_data : rtc_data; |
else if ((scop_ack)|(oled_ack)) |
slow_data <= (scop_ack)?scop_data:oled_data; |
else |
slow_data <= io_data; |
|
// |
// Peripheral stall lines |
// |
// As per the wishbone spec, these cannot be clocked or delayed. They |
// *must* be done via combinatorial logic. |
// |
wire io_stall, scop_stall, oled_stall, |
rtc_stall, sdcard_stall, |
netp_stall, gps_stall, mio_stall, cfg_stall, netb_stall, |
mem_stall, flash_stall, ram_stall, |
many_stall; |
assign wb_stall = (wb_cyc)&&( |
((io_sel)&&(io_stall)) // Never stalls |
||((scop_sel)&&(scop_stall)) // Never stalls |
||((rtc_sel)&&(rtc_stall)) // Never stalls |
||((sdcard_sel)&&(sdcard_stall))// Never stalls |
||((netp_sel)&&(netp_stall)) |
||((gps_sel)&&(gps_stall)) //(maybe? never stalls?) |
||((oled_sel)&&(oled_stall)) |
||((mio_sel)&&(mio_stall)) |
||((cfg_sel)&&(cfg_stall)) |
||((netb_sel)&&(netb_stall)) // Never stalls |
||((mem_sel)&&(mem_stall)) // Never stalls |
||((flash_sel|flctl_sel)&&(flash_stall)) |
||((ram_sel)&&(ram_stall))); |
|
|
// |
// Bus Error calculation(s) |
// |
|
// Selecting nothing is only an error if the strobe line is high as well |
// as the cycle line. However, this is captured within the wb_err |
// logic itself, so we can ignore it for a line or two. |
assign none_sel = ( //(skiperr)|| |
(~|{ io_sel, scop_sel, flctl_sel, rtc_sel, |
sdcard_sel, netp_sel, gps_sel, |
oled_sel, |
mio_sel, cfg_sel, netb_sel, mem_sel, |
flash_sel,ram_sel })); |
// |
// Selecting multiple devices at once is a design flaw that should |
// never happen. Hence, if this logic won't build, we won't include |
// it. Still, having this logic in place has saved my tush more than |
// once. |
// |
reg [31:0] sel_addr; |
always @(posedge i_clk) |
sel_addr <= wb_addr; |
|
reg many_sel_a, many_sel_b, single_sel_a, single_sel_b, last_stb; |
always @(posedge i_clk) |
begin |
last_stb <= wb_stb; |
|
single_sel_a <= (wb_stb)&&((ram_sel)|(flash_sel) |
|(mem_sel)|(netb_sel)|(cfg_sel)); |
many_sel_a <= 1'b0; |
if ((ram_sel)&&((flash_sel)||(mem_sel)||(netb_sel)||cfg_sel)) |
many_sel_a <= 1'b1; |
else if ((flash_sel)&&((mem_sel)||(netb_sel)||cfg_sel)) |
many_sel_a <= 1'b1; |
else if ((mem_sel)&&((netb_sel)||cfg_sel)) |
many_sel_a <= 1'b1; |
else if ((netb_sel)&&(cfg_sel)) |
many_sel_a <= 1'b1; |
|
single_sel_b <= (wb_stb)&&((mio_sel)||(gps_sel)||(netp_sel) |
||(sdcard_sel)||(rtc_sel)||(flctl_sel) |
||(oled_sel)||(scop_sel)||(io_sel)); |
many_sel_b <= 1'b0; |
if ((mio_sel)&&((gps_sel)||(netp_sel)||(sdcard_sel)||(rtc_sel) |
||(flctl_sel)||(scop_sel)||(oled_sel)||(io_sel))) |
many_sel_b <= 1'b1; |
else if ((gps_sel)&&((netp_sel)||(sdcard_sel)||(rtc_sel) |
||(flctl_sel)||(scop_sel)||(oled_sel)||(io_sel))) |
many_sel_b <= 1'b1; |
else if ((netp_sel)&&((sdcard_sel)||(rtc_sel) |
||(flctl_sel)||(scop_sel)||(oled_sel)||(io_sel))) |
many_sel_b <= 1'b1; |
else if ((sdcard_sel)&&((rtc_sel) |
||(flctl_sel)||(scop_sel)||(oled_sel)||(io_sel))) |
many_sel_b <= 1'b1; |
else if ((rtc_sel)&&((flctl_sel)||(scop_sel)||(oled_sel)||(io_sel))) |
many_sel_b <= 1'b1; |
else if ((flctl_sel)&&((scop_sel)||(oled_sel)||(io_sel))) |
many_sel_b <= 1'b1; |
else if ((scop_sel)&&((oled_sel)||(io_sel))) |
many_sel_b <= 1'b1; |
else if ((oled_sel)&&(io_sel)) |
many_sel_b <= 1'b1; |
end |
|
wire sel_err; // 5 inputs |
assign sel_err = ( (last_stb)&&(~single_sel_a)&&(~single_sel_b)) |
||((single_sel_a)&&(single_sel_b)) |
||((single_sel_a)&&(many_sel_a)) |
||((single_sel_b)&&(many_sel_b)); |
assign wb_err = (wb_cyc)&&(sel_err || many_ack || slow_many_ack); |
|
|
// Finally, if we ever encounter a bus error, knowing the address of |
// the error will be important to figuring out how to fix it. Hence, |
// we grab it here. Be aware, however, that this might not truly be |
// the address that caused an error: in the case of none_sel it will |
// be, but if many_ack or slow_many_ack are true then we might just be |
// looking at an address on the bus that was nearby the one requested. |
reg [31:0] bus_err_addr; |
initial bus_err_addr = 32'h00; |
always @(posedge i_clk) |
if (wb_err) |
bus_err_addr <= sel_addr; |
|
// |
// I/O peripheral |
// |
// The I/O processor, herein called an fastio. This is a unique |
// set of peripherals--these are all of the peripherals that can answer |
// in a single clock--or, rather, they are the peripherals that can |
// answer the bus before their clock. Hence, the fastio simply consists |
// of a mux that selects between various peripheral responses. Further, |
// these peripherals are not allowed to stall the bus. |
// |
// There is no option for turning these off--they will always be on. |
wire [8:0] master_ints; |
assign master_ints = { zip_cpu_int, oled_int, rtc_int, sdcard_int, |
enet_tx_int, enet_rx_int, |
scop_int, flash_int, rtc_pps }; |
wire [5:0] board_ints; |
wire [3:0] w_led; |
wire rtc_ppd; |
fastio #( |
.AUXUART_SETUP(30'hd50), |
.GPSUART_SETUP(30'hd20833) |
) runio(i_clk, i_sw, i_btn, |
w_led, o_clr_led0, o_clr_led1, o_clr_led2, o_clr_led3, |
i_aux_rx, o_aux_tx, o_aux_cts, i_gps_rx, o_gps_tx, |
wb_cyc, (io_sel)&&(wb_stb), wb_we, wb_addr[4:0], |
wb_data, io_ack, io_stall, io_data, |
rtc_ppd, |
bus_err_addr, master_ints, w_interrupt, |
board_ints); |
assign { gpio_int, auxrx_int, auxtx_int, gpsrx_int, sw_int, btn_int } = board_ints; |
|
/* |
reg [25:0] dbg_counter_err, dbg_counter_cyc, dbg_counter_sel, |
dbg_counter_many; |
// assign wb_err = (wb_cyc)&&(sel_err || many_ack || slow_many_ack); |
always @(posedge i_clk) |
if (wbu_cyc) |
dbg_counter_cyc <= 0; |
else if (!dbg_counter_cyc[25]) |
dbg_counter_cyc <= dbg_counter_cyc+26'h1; |
always @(posedge i_clk) |
if (wbu_err) |
dbg_counter_err <= 0; |
else if (!dbg_counter_err[25]) |
dbg_counter_err <= dbg_counter_err+26'h1; |
always @(posedge i_clk) |
if ((wb_cyc)&&(sel_err)) |
dbg_counter_sel <= 0; |
else if (!dbg_counter_sel[25]) |
dbg_counter_sel <= dbg_counter_sel+26'h1; |
always @(posedge i_clk) |
if ((wb_cyc)&&(many_ack)) |
dbg_counter_many <= 0; |
else if (!dbg_counter_many[25]) |
dbg_counter_many <= dbg_counter_many+26'h1; |
assign o_led = { |
(!dbg_counter_many[25])|w_led[3], |
(!dbg_counter_sel[25])|w_led[2], |
(!dbg_counter_cyc[25])|w_led[1], |
(!dbg_counter_err[25])|w_led[0] }; |
*/ |
assign o_led = w_led; |
|
|
// |
// |
// Real Time Clock (RTC) device level access |
// |
// |
wire gps_tracking, ck_pps; |
wire [63:0] gps_step; |
`ifdef RTC_ACCESS |
rtcgps #(32'h15798f) // 2^48 / 200MHz |
thertc(i_clk, |
wb_cyc, (wb_stb)&&(rtc_sel), wb_we, |
wb_addr[1:0], wb_data, |
rtc_data, rtc_int, rtc_ppd, |
gps_tracking, ck_pps, gps_step[47:16], rtc_pps); |
`else |
assign rtc_data = 32'h00; |
assign rtc_int = 1'b0; |
assign rtc_pps = 1'b0; |
assign rtc_ppd = 1'b0; |
`endif |
reg r_rtc_ack; |
initial r_rtc_ack = 1'b0; |
always @(posedge i_clk) |
r_rtc_ack <= (wb_stb)&&(rtc_sel); |
assign rtc_ack = r_rtc_ack; |
assign rtc_stall = 1'b0; |
|
// |
// |
// SDCard device level access |
// |
// |
`ifdef SDCARD_ACCESS |
wire [31:0] sd_dbg; |
// SPI mapping |
wire w_sd_cs_n, w_sd_mosi, w_sd_miso; |
|
sdspi sdctrl(i_clk, |
wb_cyc, (wb_stb)&&(sdcard_sel), wb_we, |
wb_addr[1:0], wb_data, |
sdcard_ack, sdcard_stall, sdcard_data, |
w_sd_cs_n, o_sd_sck, w_sd_mosi, w_sd_miso, |
sdcard_int, 1'b1, sd_dbg); |
assign w_sd_miso = i_sd_data[0]; |
assign o_sd_data = { w_sd_cs_n, 3'b111 }; |
assign o_sd_cmd = w_sd_mosi; |
`else |
reg r_sdcard_ack; |
always @(posedge i_clk) |
r_sdcard_ack <= (wb_stb)&&(sdcard_sel); |
assign sdcard_ack = r_sdcard_ack; |
|
assign sdcard_data = 32'h00; |
assign sdcard_stall= 1'b0; |
assign sdcard_int = 1'b0; |
`endif |
|
// |
// |
// OLEDrgb device control |
// |
// |
`ifdef OLEDRGB_ACCESS |
wboled rgbctrl(i_clk, |
wb_cyc, (wb_stb)&&(oled_sel), wb_we, |
wb_addr[1:0], wb_data, |
oled_ack, oled_stall, oled_data, |
o_oled_sck, o_oled_cs_n, o_oled_mosi, o_oled_dcn, |
{ o_oled_reset_n, o_oled_vccen, o_oled_pmoden }, |
oled_int); |
`else |
assign o_oled_cs_n = 1'b1; |
assign o_oled_sck = 1'b1; |
assign o_oled_mosi = 1'b1; |
assign o_oled_dcn = 1'b1; |
assign o_oled_reset_n = 1'b0; |
assign o_oled_vccen = 1'b0; |
assign o_oled_pmoden = 1'b0; |
|
reg r_oled_ack; |
always @(posedge i_clk) |
r_oled_ack <= (wb_stb)&&(oled_sel); |
assign oled_ack = r_oled_ack; |
|
assign oled_data = 32'h00; |
assign oled_stall= 1'b0; |
assign oled_int = 1'b0; |
`endif |
|
// |
// |
// GPS CLOCK CONTROLS, BOTH THE TEST BENCH AND THE CLOCK ITSELF |
// |
// |
wire [63:0] gps_now, gps_err; |
wire [31:0] gck_data, gtb_data; |
wire gck_ack, gck_stall, gtb_ack, gtb_stall; |
`ifdef GPS_CLOCK |
// |
// GPS CLOCK SCHOOL TESTING |
// |
wire gps_pps, tb_pps, gps_locked; |
wire [1:0] gps_dbg_tick; |
|
gpsclock_tb ppscktb(i_clk, ck_pps, tb_pps, |
(wb_stb)&&(gps_sel)&&(wb_addr[3]), |
wb_we, wb_addr[2:0], |
wb_data, gtb_ack, gtb_stall, gtb_data, |
gps_err, gps_now, gps_step); |
`ifdef GPSTB |
assign gps_pps = tb_pps; // Let the truth come from our test bench |
`else |
assign gps_pps = i_gps_pps; |
`endif |
wire gps_led; |
|
// |
// GPS CLOCK CONTROL |
// |
gpsclock ppsck(i_clk, 1'b0, gps_pps, ck_pps, gps_led, |
(wb_stb)&&(gps_sel)&&(~wb_addr[3]), |
wb_we, wb_addr[1:0], |
wb_data, gck_ack, gck_stall, gck_data, |
gps_tracking, gps_now, gps_step, gps_err, gps_locked, |
gps_dbg_tick); |
`else |
|
assign gps_err = 64'h0; |
assign gps_now = 64'h0; |
assign gck_data = 32'h0; |
assign gtb_data = 32'h0; |
assign gtb_stall = 1'b0; |
assign gck_stall = 1'b0; |
assign ck_pps = 1'b0; |
|
assign gps_tracking = 1'b0; |
// Appropriate step for a 200MHz clock |
assign gps_step = { 16'h00, 32'h015798e, 16'h00 }; |
|
reg r_gck_ack; |
always @(posedge i_clk) |
r_gck_ack <= (wb_stb)&&(gps_sel); |
assign gck_ack = r_gck_ack; |
assign gtb_ack = r_gck_ack; |
|
`endif |
|
assign gps_ack = (gck_ack | gtb_ack); |
assign gps_stall = (gck_stall | gtb_stall); |
assign gps_data = (gck_ack) ? gck_data : gtb_data; |
|
|
// |
// ETHERNET DEVICE ACCESS |
// |
`ifdef ETHERNET_ACCESS |
reg r_mio_ack, r_netb_ack, r_netp_ack; |
always @(posedge i_clk) |
r_mio_ack <= (wb_stb)&&(mio_sel); |
always @(posedge i_clk) |
r_netp_ack <= (wb_stb)&&(netp_sel); |
assign mio_ack = r_mio_ack; |
assign netp_ack = r_netp_ack; |
|
assign mio_data = 32'h00; |
assign netp_data = 32'h00; |
assign mio_stall = 1'b0; |
assign netp_stall= 1'b0; |
assign enet_rx_int = 1'b0; |
assign enet_tx_int = 1'b0; |
|
enetctrl #(3) |
mdio(i_clk, i_rst, wb_cyc, (wb_stb)&&(netb_sel), wb_we, |
wb_addr[4:0], wb_data[15:0], |
netb_ack, netb_stall, netb_data, |
o_mdclk, o_mdio, i_mdio, o_mdwe); |
`else |
reg r_mio_ack, r_netb_ack, r_netp_ack; |
always @(posedge i_clk) |
r_mio_ack <= (wb_stb)&&(mio_sel); |
always @(posedge i_clk) |
r_netp_ack <= (wb_stb)&&(netp_sel); |
assign mio_ack = r_mio_ack; |
assign netp_ack = r_netp_ack; |
|
assign mio_data = 32'h00; |
assign netp_data = 32'h00; |
assign mio_stall = 1'b0; |
assign netp_stall= 1'b0; |
assign enet_rx_int = 1'b0; |
assign enet_tx_int = 1'b0; |
|
// |
// 2kW memory, 1kW for each of transmit and receive. (Max pkt length |
// is 512W, so this allows for two 512W in memory.) Since we don't |
// really have ethernet without ETHERNET_ACCESS defined, this just |
// consumes resources for us so we have an idea of what might be |
// available when we do have ETHERNET_ACCESS defined. |
// |
memdev #(11) enet_buffers(i_clk, wb_cyc, (wb_stb)&&(netb_sel), wb_we, |
wb_addr[10:0], wb_data, netb_ack, netb_stall, netb_data); |
assign o_mdclk = 1'b1; |
assign o_mdio = 1'b1; |
assign o_mdwe = 1'b1; |
|
`endif |
|
|
// |
// MULTIBOOT/ICAPE2 CONFIGURATION ACCESS |
// |
`ifdef ICAPE_ACCESS |
wbicapetwo fpga_cfg(i_clk, wb_cyc,(cfg_sel)&&(wb_stb), wb_we, |
wb_addr[4:0], wb_data, |
cfg_ack, cfg_stall, cfg_data); |
`else |
reg r_cfg_ack; |
always @(posedge i_clk) |
r_cfg_ack <= (cfg_sel)&&(wb_stb); |
assign cfg_ack = r_cfg_ack; |
assign cfg_stall = 1'b0; |
assign cfg_data = 32'h00; |
`endif |
|
// |
// RAM MEMORY ACCESS |
// |
// There is no option to turn this off--this RAM must always be |
// present in the design. |
memdev #(15) // 32kW, or 128kB, 15 address lines |
blkram(i_clk, wb_cyc, (wb_stb)&&(mem_sel), wb_we, wb_addr[14:0], |
wb_data, mem_ack, mem_stall, mem_data); |
|
// |
// FLASH MEMORY ACCESS |
// |
`ifdef FLASH_ACCESS |
`ifdef FLASH_SCOPE |
wire [31:0] flash_debug; |
`endif |
wire w_ignore_cmd_accepted; |
eqspiflash flashmem(i_clk, i_rst, |
wb_cyc,(wb_stb)&&(flash_sel),(wb_stb)&&(flctl_sel),wb_we, |
wb_addr[21:0], wb_data, |
flash_ack, flash_stall, flash_data, |
o_qspi_sck, o_qspi_cs_n, o_qspi_mod, o_qspi_dat, i_qspi_dat, |
flash_int, w_ignore_cmd_accepted |
`ifdef FLASH_SCOPE |
, flash_debug |
`endif |
); |
`else |
assign o_qspi_sck = 1'b1; |
assign o_qspi_cs_n= 1'b1; |
assign o_qspi_mod = 2'b01; |
assign o_qspi_dat = 4'h0; |
assign flash_data = 32'h00; |
assign flash_stall = 1'b0; |
assign flash_int = 1'b0; |
|
reg r_flash_ack; |
always @(posedge i_clk) |
r_flash_ack <= (wb_stb)&&(flash_sel); |
assign flash_ack = r_flash_ack; |
`endif |
|
|
// |
// |
// DDR3-SDRAM |
// |
// |
`ifdef SDRAM_ACCESS |
wbddrsdram rami(i_clk, |
wb_cyc, (wb_stb)&&(ram_sel), wb_we, wb_addr[25:0], wb_data, |
ram_ack, ram_stall, ram_data, |
o_ddr_reset_n, o_ddr_cke, |
o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n, |
o_ddr_dqs, |
o_ddr_addr, o_ddr_ba, o_ddr_data, i_ddr_data); |
`else |
assign ram_data = 32'h00; |
assign ram_stall = 1'b0; |
reg r_ram_ack; |
always @(posedge i_clk) |
r_ram_ack <= (wb_stb)&&(ram_sel); |
assign ram_ack = r_ram_ack; |
|
// And idle the DDR3 SDRAM |
assign o_ddr_reset_n = 1'b0; // Leave the SDRAM in reset |
assign o_ddr_cke = 1'b0; // Disable the SDRAM clock |
// DQS |
assign o_ddr_dqs = 3'b100; // Leave DQS pins in high impedence |
// DDR3 control wires (not enabled if CKE=0) |
assign o_ddr_cs_n = 1'b0; // NOOP command |
assign o_ddr_ras_n = 1'b1; |
assign o_ddr_cas_n = 1'b1; |
assign o_ddr_we_n = 1'b1; |
// (Unused) data wires |
assign o_ddr_addr = 14'h00; |
assign o_ddr_ba = 3'h0; |
assign o_ddr_data = 32'h00; |
`endif |
|
|
// |
// |
// WISHBONE SCOPES |
// |
// |
// |
// |
wire [31:0] scop_a_data; |
wire scop_a_ack, scop_a_stall, scop_a_interrupt; |
`ifdef CPU_SCOPE |
wire [31:0] scop_cpu_data; |
wire scop_cpu_ack, scop_cpu_stall, scop_cpu_interrupt; |
wire scop_cpu_trigger; |
// assign scop_cpu_trigger = zip_scope_data[30]; |
assign scop_cpu_trigger = (wb_stb)&&(mem_sel)&&(~wb_we) |
&&(wb_err)||(zip_scope_data[31]); |
wbscope #(5'd13) cpuscope(i_clk, 1'b1,(scop_cpu_trigger), zip_scope_data, |
// Wishbone interface |
i_clk, wb_cyc, ((wb_stb)&&(scop_sel)&&(wb_addr[2:1]==2'b00)), wb_we, wb_addr[0], |
wb_data, |
scop_cpu_ack, scop_cpu_stall, scop_cpu_data, |
scop_cpu_interrupt); |
|
assign scop_a_data = scop_cpu_data; |
assign scop_a_ack = scop_cpu_ack; |
assign scop_a_stall = scop_cpu_stall; |
assign scop_a_interrupt = scop_cpu_interrupt; |
`else |
`ifdef FLASH_SCOPE |
wire [31:0] scop_flash_data; |
wire scop_flash_ack, scop_flash_stall, scop_flash_interrupt; |
wire scop_flash_trigger; |
// assign scop_cpu_trigger = zip_scope_data[30]; |
assign scop_flash_trigger = (wb_stb)&&((flash_sel)||(flctl_sel)); |
wbscope #(5'd13) flashscope(i_clk, 1'b1, |
(scop_flash_trigger), flash_debug, |
// Wishbone interface |
i_clk, wb_cyc, ((wb_stb)&&(scop_sel)&&(wb_addr[2:1]==2'b00)), wb_we, wb_addr[0], |
wb_data, |
scop_flash_ack, scop_flash_stall, scop_flash_data, |
scop_flash_interrupt); |
|
assign scop_a_data = scop_flash_data; |
assign scop_a_ack = scop_flash_ack; |
assign scop_a_stall = scop_flash_stall; |
assign scop_a_interrupt = scop_flash_interrupt; |
`else |
reg r_scop_a_ack; |
always @(posedge i_clk) |
r_scop_a_ack <= (wb_stb)&&(scop_sel)&&(wb_addr[2:1] == 2'b00); |
assign scop_a_data = 32'h00; |
assign scop_a_ack = r_scop_a_ack; |
assign scop_a_stall = 1'b0; |
assign scop_a_interrupt = 1'b0; |
`endif |
`endif |
|
wire [31:0] scop_b_data; |
wire scop_b_ack, scop_b_stall, scop_b_interrupt; |
`ifdef GPS_SCOPE |
reg [18:0] r_gps_debug; |
wire [31:0] scop_gps_data; |
wire scop_gps_ack, scop_gps_stall, scop_gps_interrupt; |
always @(posedge i_clk) |
r_gps_debug <= { |
gps_dbg_tick, gps_tracking, gps_locked, |
gpu_data[7:0], |
// (wb_cyc)&&(wb_stb)&&(io_sel), |
(wb_stb)&&(io_sel)&&(wb_addr[4:3]==2'b11)&&(wb_we), |
(wb_stb)&&(gps_sel)&&(wb_addr[3:2]==2'b01), |
gpu_int, |
i_gps_rx, rtc_pps, ck_pps, i_gps_pps }; |
wbscopc #(5'd13,19,32,1) gpsscope(i_clk, 1'b1, ck_pps, r_gps_debug, |
// Wishbone interface |
i_clk, wb_cyc, ((wb_stb)&&(scop_sel)&&(wb_addr[2:1]==2'b01)), |
wb_we, wb_addr[0], wb_data, |
scop_gps_ack, scop_gps_stall, scop_gps_data, |
scop_gps_interrupt); |
`else |
assign scop_b_data = 32'h00; |
assign scop_b_stall = 1'b0; |
assign scop_b_interrupt = 1'b0; |
|
reg r_scop_b_ack; |
always @(posedge i_clk) |
r_scop_b_ack <= (wb_stb)&&(scop_sel)&&(wb_addr[2:1] == 2'b01); |
assign scop_b_ack = r_scop_b_ack; |
`endif |
|
// |
// SCOPE C |
// |
wire [31:0] scop_c_data; |
wire scop_c_ack, scop_c_stall, scop_c_interrupt; |
// |
//`else |
assign scop_c_data = 32'h00; |
assign scop_c_stall = 1'b0; |
assign scop_c_interrupt = 1'b0; |
|
reg r_scop_c_ack; |
always @(posedge i_clk) |
r_scop_c_ack <= (wb_stb)&&(scop_sel)&&(wb_addr[2:1] == 2'b10); |
assign scop_c_ack = r_scop_c_ack; |
//`endif |
|
// |
// SCOPE D |
// |
wire [31:0] scop_d_data; |
wire scop_d_ack, scop_d_stall, scop_d_interrupt; |
// |
//`else |
assign scop_d_data = 32'h00; |
assign scop_d_stall = 1'b0; |
assign scop_d_interrupt = 1'b0; |
|
reg r_scop_d_ack; |
always @(posedge i_clk) |
r_scop_d_ack <= (wb_stb)&&(scop_sel)&&(wb_addr[2:1] == 2'b11); |
assign scop_d_ack = r_scop_d_ack; |
//`endif |
|
assign scop_int = scop_a_interrupt |
|| scop_b_interrupt |
|| scop_c_interrupt |
|| scop_d_interrupt; |
assign scop_stall = ((wb_addr[2:1]==2'b0)?scop_a_stall |
: ((wb_addr[2:1]==2'b01)?scop_b_stall |
: ((wb_addr[2:1]==2'b11)?scop_c_stall |
: scop_d_stall))); // Will always be 1'b0; |
initial scop_ack = 1'b0; |
always @(posedge i_clk) |
scop_ack <= scop_a_ack | scop_b_ack | scop_c_ack | scop_d_ack; |
always @(posedge i_clk) |
if (scop_a_ack) |
scop_data <= scop_a_data; |
else if (scop_b_ack) |
scop_data <= scop_b_data; |
else if (scop_c_ack) |
scop_data <= scop_c_data; |
else // if (scop_d_ack) |
scop_data <= scop_d_data; |
|
endmodule |
/rtl/bigadd.v
0,0 → 1,66
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: bigadd.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 bigadd(i_clk, i_sync, i_a, i_b, o_r, o_sync); |
input i_clk, i_sync; |
input [63:0] i_a, i_b; |
output reg [63:0] o_r; |
output reg o_sync; |
|
reg r_sync, r_pps; |
reg [31:0] r_hi_a, r_hi_b, r_low; |
|
initial r_sync = 1'b0; |
always @(posedge i_clk) |
r_sync <= i_sync; |
|
always @(posedge i_clk) |
{ r_pps, r_low } <= i_a[31:0] + i_b[31:0]; |
always @(posedge i_clk) |
r_hi_a <= i_a[63:32]; |
always @(posedge i_clk) |
r_hi_b <= i_b[63:32]; |
|
initial o_sync = 1'b0; |
always @(posedge i_clk) |
o_sync <= r_sync; |
always @(posedge i_clk) |
o_r[31:0] <= r_low; |
always @(posedge i_clk) |
o_r[63:32] <= r_hi_a + r_hi_b + { 31'h00, r_pps }; |
|
endmodule |
/rtl/bigsmpy.v
0,0 → 1,117
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: bigsmpy.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 bigsmpy(i_clk, i_sync, i_sgn, i_a, i_b, o_r, o_sync); |
input i_clk, i_sync, i_sgn; |
input [31:0] i_a, i_b; |
output reg [63:0] o_r; |
output reg o_sync; |
|
// |
// A pipeline, shift register, to track our synchronization pulse |
reg [3:0] r_s; |
|
// |
reg r_mpy_signed; |
reg [31:0] r_mpy_a_input, r_mpy_b_input; |
always @(posedge i_clk) |
begin |
if (i_sgn) |
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 |
|
r_mpy_signed <= i_sgn; |
r_s[0] <= i_sync; |
end |
|
reg [31:0] pp_f, pp_o, pp_i, pp_l; |
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_o <= r_mpy_a_input[31:16] * r_mpy_b_input[15: 0]; |
pp_i <= 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]; |
|
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; |
r_s[1] <= r_s[0]; |
end |
|
reg [32:0] partial_mpy_oi, partial_mpy_lo; |
reg [31:0] partial_mpy_hi; |
always @(posedge i_clk) |
begin |
partial_mpy_lo[30: 0] <= pp_l[30:0]; |
partial_mpy_lo[32:31] <= pp_s[0] + pp_l[31]; |
partial_mpy_oi[32: 0] <= pp_o + pp_i; |
partial_mpy_hi[31: 0] <= pp_s[32:1] + pp_f; |
r_s[2] <= r_s[1]; |
end |
|
reg partial_mpy_2cl, partial_mpy_2ch; |
reg [31:0] partial_mpy_2lo, partial_mpy_2hi; |
always @(posedge i_clk) |
begin |
partial_mpy_2lo[15:0] <= partial_mpy_lo[15:0]; |
{ partial_mpy_2cl, partial_mpy_2lo[31:16] } |
<= { 1'b0, partial_mpy_oi[15:0]}+ partial_mpy_lo[32:16]; |
{ partial_mpy_2ch, partial_mpy_2hi[16:0] } |
<= partial_mpy_oi[32:16] + partial_mpy_hi[16:0]; |
partial_mpy_2hi[31:16] <= { partial_mpy_2hi[31:17], 1'b0 }; |
r_s[3] <= r_s[2]; |
end |
|
always @(posedge i_clk) |
begin |
o_r[31: 0] <= partial_mpy_2lo[31:0]; |
o_r[63:32] <= partial_mpy_2hi |
+ { 13'h0, partial_mpy_2ch, 1'b0, |
15'h0, partial_mpy_2cl }; |
o_sync <= r_s[3]; |
end |
|
|
endmodule |
/rtl/bigsub.v
0,0 → 1,66
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: bigsub.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 bigsub(i_clk, i_sync, i_a, i_b, o_r, o_sync); |
input i_clk, i_sync; |
input [63:0] i_a, i_b; |
output reg [63:0] o_r; |
output reg o_sync; |
|
reg r_sync, r_pps; |
reg [31:0] r_hi_a, r_hi_b, r_low; |
|
initial r_sync = 1'b0; |
always @(posedge i_clk) |
r_sync <= i_sync; |
|
always @(posedge i_clk) |
{ r_pps, r_low } <= i_a[31:0] + ({1'b1,~i_b[31:0]}) + 1'b1; |
always @(posedge i_clk) |
r_hi_a <= i_a[63:32]; |
always @(posedge i_clk) |
r_hi_b <= ~i_b[63:32]; |
|
initial o_sync = 1'b0; |
always @(posedge i_clk) |
o_sync <= r_sync; |
always @(posedge i_clk) |
o_r[31:0] <= r_low; |
always @(posedge i_clk) |
o_r[63:32] <= r_hi_a + r_hi_b + { 31'h00, r_pps }; |
|
endmodule |
/rtl/lloled.v
0,0 → 1,222
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: lloled.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
`define OLED_IDLE 3'h0 |
`define OLED_START 3'h1 |
`define OLED_BITS 3'h2 |
`define OLED_READY 3'h3 |
`define OLED_STOP 3'h4 |
`define OLED_STOP_B 3'h5 |
|
// Modes |
`define OLED_MOD_SPI 2'b00 |
`define OLED_MOD_QOUT 2'b10 |
`define OLED_MOD_QIN 2'b11 |
|
module lloled(i_clk, |
// Module interface |
i_wr, i_dbit, i_word, i_len, o_busy, |
// OLED interface |
o_sck, o_cs_n, o_mosi, o_dbit); |
parameter CTRBITS = 8; |
input i_clk; |
// Chip interface |
// Can send info |
// i_wr = 1, |
// i_word = { 1'b0, 32'info to send }, |
// i_len = # of bytes in word-1 |
input i_wr, i_dbit; |
input [31:0] i_word; |
input [1:0] i_len; // 0=>8bits, 1=>16 bits, 2=>24 bits, 3=>32 bits |
output reg o_busy; |
// Interface with the OLED lines |
output reg o_sck, o_cs_n, o_mosi, o_dbit; |
|
// Timing: |
// |
// Tick Clk BSY/WR CS_n BIT/MO STATE |
// 0 1 0/0 1 - |
// 1 1 0/1 1 - |
// 2 1 1/0 0 - OLED_START |
// 3 0 1/0 0 - OLED_START |
// 4 0 1/0 0 0 OLED_BITS |
// 5 1 1/0 0 0 OLED_BITS |
// 6 0 1/0 0 1 OLED_BITS |
// 7 1 1/0 0 1 OLED_BITS |
// 8 0 1/0 0 2 OLED_BITS |
// 9 1 1/0 0 2 OLED_BITS |
// 10 0 1/0 0 3 OLED_BITS |
// 11 1 1/0 0 3 OLED_BITS |
// 12 0 1/0 0 4 OLED_BITS |
// 13 1 1/0 0 4 OLED_BITS |
// 14 0 1/0 0 5 OLED_BITS |
// 15 1 1/0 0 5 OLED_BITS |
// 16 0 1/0 0 6 OLED_BITS |
// 17 1 1/1 0 6 OLED_BITS |
// 18 0 1/1 0 7 OLED_READY |
// 19 1 0/1 0 7 OLED_READY |
// 20 0 1/0/V 0 8 OLED_BITS |
// 21 1 1/0 0 8 OLED_BITS |
// 22 0 1/0 0 9 OLED_BITS |
// 23 1 1/0 0 9 OLED_BITS |
// 24 0 1/0 0 10 OLED_BITS |
// 25 1 1/0 0 10 OLED_BITS |
// 26 0 1/0 0 11 OLED_BITS |
// 27 1 1/0 0 11 OLED_BITS |
// 28 0 1/0 0 12 OLED_BITS |
// 29 1 1/0 0 12 OLED_BITS |
// 30 0 1/0 0 13 OLED_BITS |
// 31 1 1/0 0 13 OLED_BITS |
// 32 0 1/0 0 14 OLED_BITS |
// 33 1 1/0 0 14 OLED_BITS |
// 34 0 1/0 0 15 OLED_READY |
// 35 1 1/0 0 15 OLED_READY |
// 36 1 1/0/V 0 - OLED_STOP |
// 37 1 1/0 0 - OLED_STOPB |
// 38 1 1/0 1 - OLED_IDLE |
// 39 1 0/0 1 - |
|
reg [5:0] spi_len; |
reg [31:0] r_word; |
reg [2:0] state; |
initial state = `OLED_IDLE; |
initial o_sck = 1'b1; |
initial o_cs_n = 1'b1; |
initial o_mosi = 1'b0; |
initial o_busy = 1'b0; |
|
reg [(CTRBITS-1):0] counter; |
reg last_counter, pre_last_counter; |
always @(posedge i_clk) // Clock cycle time > 150 ns > 300 ticks |
last_counter <= (counter == {{(CTRBITS-1){1'b0}},1'b1}); |
always @(posedge i_clk) |
pre_last_counter <= (counter == {{(CTRBITS-2){1'b0}},2'b10}); |
always @(posedge i_clk) |
if (state == `OLED_IDLE) |
counter <= {(CTRBITS){1'b1}}; |
else |
counter <= counter + {(CTRBITS){1'b1}}; |
always @(posedge i_clk) |
if ((state == `OLED_IDLE)&&(o_sck)) |
begin |
o_cs_n <= 1'b1; |
o_busy <= 1'b0; |
r_word <= i_word; |
spi_len<= { 1'b0, i_len, 3'b000 } + 6'h8; |
o_sck <= 1'b1; |
o_dbit <= i_dbit; |
if (i_wr) |
begin |
state <= `OLED_START; |
o_cs_n <= 1'b0; |
o_busy <= 1'b1; |
end |
end else if (state == `OLED_START) |
begin // We come in here with sck high, stay here 'til sck is low |
o_sck <= 1'b0; |
if (o_sck == 1'b0) |
begin |
state <= `OLED_BITS; |
spi_len<= spi_len - 6'h1; |
r_word <= { r_word[30:0], 1'b0 }; |
end |
o_cs_n <= 1'b0; |
o_busy <= 1'b1; |
o_mosi <= r_word[31]; |
end else if (~last_counter) |
begin |
o_busy <= (pre_last_counter)&&(o_sck) |
&&((state != `OLED_READY)||(~i_wr)); |
end else if (~o_sck) |
begin |
o_sck <= 1'b1; |
o_busy <= 1'b1; |
end else if (state == `OLED_BITS) |
begin |
// Should enter into here with at least a spi_len |
// of one, perhaps more |
o_sck <= 1'b0; |
o_busy <= 1'b1; |
o_mosi <= r_word[31]; |
r_word <= { r_word[30:0], 1'b0 }; |
spi_len <= spi_len - 6'h1; |
if (spi_len == 6'h1) |
state <= `OLED_READY; |
end else if (state == `OLED_READY) |
begin |
o_cs_n <= 1'b0; |
o_busy <= 1'b1; |
// This is the state on the last clock (both low and |
// high clocks) of the data. Data is valid during |
// this state. Here we chose to either STOP or |
// continue and transmit more. |
o_sck <= 1'b0; |
if((~o_busy)&&(i_wr))// Acknowledge a new request |
begin |
state <= `OLED_BITS; |
o_busy <= 1'b1; |
o_sck <= 1'b0; |
|
// Set up the first bits on the bus |
o_mosi <= i_word[31]; |
r_word <= { i_word[30:0], 1'b0 }; |
spi_len<= { 1'b0, i_len, 3'b111 }; |
|
// Read a bit upon any transition |
end else begin |
o_sck <= 1'b1; |
state <= `OLED_STOP; |
o_busy <= 1'b1; |
end |
end else if (state == `OLED_STOP) |
begin |
o_sck <= 1'b1; // Stop the clock |
o_busy <= 1'b1; // Still busy till port is clear |
state <= `OLED_STOP_B; |
end else // if (state == `OLED_STOP_B) |
begin |
o_cs_n <= 1'b1; // Deselect CS |
o_sck <= 1'b1; |
// Do I need this???? |
// spi_len <= 3; // Minimum CS high time before next cmd |
state <= `OLED_IDLE; |
o_mosi <= 1'b1; |
o_busy <= 1'b1; |
end |
/* |
*/ |
|
endmodule |
|
/rtl/wbuoutput.v
0,0 → 1,109
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbuoutput.v |
// |
// Project: FPGA library |
// |
// Purpose: Converts 36-bit codewords into bytes to be placed on the serial |
// output port. The codewords themselves are the results of bus |
// transactions, which are then (hopefully) compressed within here and |
// carefully arranged into "lines" for visual viewing (if necessary). |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
module wbuoutput(i_clk, i_rst, i_stb, i_codword, |
i_wb_cyc, i_int, i_bus_busy, |
o_stb, o_char, i_tx_busy, o_fifo_err); |
input i_clk, i_rst; |
input i_stb; |
input [35:0] i_codword; |
// Not Idle indicators |
input i_wb_cyc, i_int, i_bus_busy; |
// Outputs to our UART transmitter |
output wire o_stb; |
output wire [7:0] o_char; |
// Miscellaneous I/O: UART transmitter busy, and fifo error |
input i_tx_busy; |
output wire o_fifo_err; |
|
wire fifo_rd, dw_busy, fifo_empty_n, fifo_err; |
wire [35:0] fifo_codword; |
|
wire cw_stb, cw_busy, cp_stb, dw_stb, ln_stb, ln_busy, |
cp_busy, byte_busy; |
wire [35:0] cw_codword, cp_word; |
wire [6:0] dw_bits, ln_bits; |
|
// `define SKIP_FIFO |
`ifdef SKIP_FIFO |
assign fifo_rd = i_stb; |
assign fifo_codword = i_codword; |
assign fifo_err = 1'b0; |
`else |
assign fifo_rd = (fifo_empty_n)&&(~cw_busy); |
wbufifo #(36,10) busoutfifo(i_clk, i_rst, i_stb, i_codword, |
fifo_rd, fifo_codword, fifo_empty_n, |
fifo_err); |
`endif |
|
assign o_fifo_err = fifo_err; |
|
wbuidleint buildcw(i_clk, fifo_rd, fifo_codword, |
i_wb_cyc, i_bus_busy, i_int, |
cw_stb, cw_codword, cw_busy, cp_busy); |
// assign o_dbg = dw_busy; // Always asserted ... ??? |
// assign o_dbg = { dw_busy, ln_busy, fifo_rd }; |
// Stuck: dw_busy and ln_busy get stuck high after read attempt, |
// fifo_rd is low |
// assign o_dbg = { fifo_rd, cp_stb, cw_stb }; |
// cw_stb and cp_stb get stuck high after one read |
|
// |
// cw_busy & cw_stb, not cp_stb, but dw_busy |
// |
|
// `define SKIP_COMPRESS |
`ifdef SKIP_COMPRESS |
assign cp_stb = cw_stb; |
assign cp_word = cw_codword; |
assign cp_busy = dw_busy; |
`else |
assign cp_busy = cp_stb; |
wbucompress packit(i_clk, cw_stb, cw_codword, |
cp_stb, cp_word, dw_busy); |
`endif |
|
wbudeword deword(i_clk, cp_stb, cp_word, ln_busy, |
dw_stb, dw_bits, dw_busy); |
|
wbucompactlines linepacker(i_clk, dw_stb, dw_bits, |
ln_stb, ln_bits, |
(i_wb_cyc||i_bus_busy||fifo_empty_n||cw_busy), |
byte_busy, ln_busy); |
|
wbusixchar mkbytes(i_clk, ln_stb, ln_bits, o_stb, o_char, byte_busy, i_tx_busy); |
|
endmodule |
/rtl/fasttop.v
0,0 → 1,469
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: fasttop.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: This is the top level Verilog file. It is so named as fasttop, |
// because my purpose will be to run the Arty at 200MHz, just to |
// prove that I can get it up to that frequency. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 fasttop(i_clk_100mhz, i_reset_btn, |
i_sw, // Switches |
i_btn, // Buttons |
o_led, // Single color LEDs |
o_clr_led0, o_clr_led1, o_clr_led2, o_clr_led3, // Color LEDs |
// RS232 UART |
i_uart_rx, o_uart_tx, |
// Quad-SPI Flash control |
o_qspi_sck, o_qspi_cs_n, io_qspi_dat, |
// Missing: Ethernet |
o_eth_mdclk, io_eth_mdio, |
// Memory |
o_ddr_reset_n, o_ddr_cke, o_ddr_ck_p, o_ddr_ck_n, |
o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n, |
io_ddr_dqs_p, io_ddr_dqs_n, |
o_ddr_addr, o_ddr_ba, |
io_ddr_data, o_ddr_dm, o_ddr_odt, |
// SD Card |
o_sd_sck, io_sd_cmd, io_sd, i_sd_cs, i_sd_wp, |
// GPS Pmod |
i_gps_pps, i_gps_3df, i_gps_rx, o_gps_tx, |
// OLED Pmod |
o_oled_sck, o_oled_cs_n, o_oled_mosi, o_oled_dcn, o_oled_reset_n, |
o_oled_vccen, o_oled_pmoden, |
// PMod I/O |
i_aux_rx, i_aux_rts, o_aux_tx, o_aux_cts |
); |
input i_clk_100mhz, i_reset_btn; |
input [3:0] i_sw; // Switches |
input [3:0] i_btn; // Buttons |
output wire [3:0] o_led; // LED |
output wire [2:0] o_clr_led0, o_clr_led1, o_clr_led2, o_clr_led3; |
// UARTs |
input i_uart_rx; |
output wire o_uart_tx; |
// Quad SPI flash |
output wire o_qspi_sck, o_qspi_cs_n; |
inout [3:0] io_qspi_dat; |
// Ethernet // Not yet implemented |
// Ethernet control (MDIO) |
output wire o_eth_mdclk; |
inout wire io_eth_mdio; |
// DDR3 SDRAM |
output wire o_ddr_reset_n; |
output wire o_ddr_cke; |
output wire o_ddr_ck_p, o_ddr_ck_n; |
output wire o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n; |
inout [1:0] io_ddr_dqs_p, io_ddr_dqs_n; |
output wire [13:0] o_ddr_addr; |
output wire [2:0] o_ddr_ba; |
inout [15:0] io_ddr_data; |
// |
output wire [1:0] o_ddr_dm; |
output wire o_ddr_odt; |
// SD Card |
output wire o_sd_sck; |
inout io_sd_cmd; |
inout [3:0] io_sd; |
input i_sd_cs; |
input i_sd_wp; |
// GPS PMod |
input i_gps_pps, i_gps_3df, i_gps_rx; |
output wire o_gps_tx; |
// OLEDRGB PMod |
output wire o_oled_sck, o_oled_cs_n, o_oled_mosi, |
o_oled_dcn, o_oled_reset_n, o_oled_vccen, |
o_oled_pmoden; |
// Aux UART |
input i_aux_rx, i_aux_rts; |
output wire o_aux_tx, o_aux_cts; |
|
// `define FULLCLOCK |
// Build our master clock |
wire i_clk, clk_for_ddr, clk2_unused, enet_clk, clk5_unused, |
clk_feedback, clk_locked; |
PLLE2_BASE #( |
.BANDWIDTH("OPTIMIZED"), // OPTIMIZED, HIGH, LOW |
.CLKFBOUT_PHASE(0.0), // Phase offset in degrees of CLKFB, (-360-360) |
.CLKIN1_PERIOD(10.0), // Input clock period in ns to ps resolution |
`ifdef FULLCLOCK |
// CLKOUT0_DIVIDE - CLKOUT5_DIVIDE: divide amount for each CLKOUT(1-128) |
.CLKFBOUT_MULT(8), // Multiply value for all CLKOUT (2-64) |
.CLKOUT0_DIVIDE(4), // 200 MHz |
.CLKOUT1_DIVIDE(4), // 200 MHz |
.CLKOUT2_DIVIDE(8), // 100 MHz |
.CLKOUT3_DIVIDE(32), // 25 MHz |
.CLKOUT4_DIVIDE(16), // 50 MHz |
.CLKOUT5_DIVIDE(24), |
`else |
// 100*64/40 = 160 -- the fastest speed where the UART will |
// still work at 4MBaud. Others will still support 115200 |
// Baud |
// 100*64/36 = 177.78 |
// 100*64/34 = 188.24 |
// 100*64/33 = 193.94 |
.CLKFBOUT_MULT(8), // Multiply value for all CLKOUT (2-64) |
.CLKOUT0_DIVIDE(5), // 160 MHz |
.CLKOUT1_DIVIDE(5), // 160 MHz |
.CLKOUT2_DIVIDE(10), // 80 MHz |
.CLKOUT3_DIVIDE(40), // 20 MHz |
.CLKOUT4_DIVIDE(20), // 40 MHz |
.CLKOUT5_DIVIDE(30), |
`endif |
// CLKOUT0_DUTY_CYCLE -- Duty cycle for each CLKOUT |
.CLKOUT0_DUTY_CYCLE(0.5), |
.CLKOUT1_DUTY_CYCLE(0.5), |
.CLKOUT2_DUTY_CYCLE(0.5), |
.CLKOUT3_DUTY_CYCLE(0.5), |
.CLKOUT4_DUTY_CYCLE(0.5), |
.CLKOUT5_DUTY_CYCLE(0.5), |
// CLKOUT0_PHASE -- phase offset for each CLKOUT |
.CLKOUT0_PHASE(0.0), |
.CLKOUT1_PHASE(90.0), |
.CLKOUT2_PHASE(0.0), |
.CLKOUT3_PHASE(0.0), |
.CLKOUT4_PHASE(0.0), |
.CLKOUT5_PHASE(0.0), |
.DIVCLK_DIVIDE(1), // Master division value , (1-56) |
.REF_JITTER1(0.0), // Reference input jitter in UI (0.000-0.999) |
.STARTUP_WAIT("FALSE") // Delayu DONE until PLL Locks, ("TRUE"/"FALSE") |
) genclock( |
// Clock outputs: 1-bit (each) output |
.CLKOUT0(i_clk), |
.CLKOUT1(clk_for_ddr), |
.CLKOUT2(clk2_unused), // Reserved for flash, should we need it |
.CLKOUT3(enet_clk), |
.CLKOUT4(clk4_unused), |
.CLKOUT5(clk5_unused), |
.CLKFBOUT(clk_feedback), // 1-bit output, feedback clock |
.LOCKED(clk_locked), |
.CLKIN1(i_clk_100mhz), |
.PWRDWN(1'b0), |
.RST(1'b0), |
.CLKFBIN(clk_feedback) // 1-bit input, feedback clock |
); |
|
// UART interface |
wire [29:0] bus_uart_setup; |
`ifdef FULLCLOCK |
assign bus_uart_setup = 30'h10000032; // 4MBaud, 7 bits |
`else |
assign bus_uart_setup = 30'h10000028;//4MBaud,7 bits,@160MHzClk |
//assign bus_uart_setup = 30'h10000019;//4MBaud,7 bits,@100MHzClk |
`endif |
|
wire [7:0] rx_data, tx_data; |
wire rx_break, rx_parity_err, rx_frame_err, rx_stb; |
wire tx_stb, tx_busy; |
|
reg pwr_reset, pre_reset; |
initial pwr_reset = 1'b1; |
initial pre_reset = 1'b0; |
always @(posedge i_clk) |
pre_reset <= ~i_reset_btn; |
always @(posedge i_clk) |
pwr_reset <= pre_reset; |
|
wire w_ck_uart, w_uart_tx; |
rxuart rcv(i_clk, pwr_reset, bus_uart_setup, i_uart_rx, |
rx_stb, rx_data, rx_break, |
rx_parity_err, rx_frame_err, w_ck_uart); |
txuart txv(i_clk, pwr_reset, bus_uart_setup, 1'b0, |
tx_stb, tx_data, o_uart_tx, tx_busy); |
|
|
|
|
|
|
////// |
// |
// |
// The WB bus interconnect, herein called fastmaster, which handles |
// just about ... everything. |
// |
// |
////// |
wire w_qspi_sck; |
wire [1:0] qspi_bmod; |
wire [3:0] qspi_dat; |
wire [3:0] i_qspi_dat; |
|
// |
wire [2:0] w_ddr_dqs; |
wire [31:0] wo_ddr_data, wi_ddr_data; |
// |
wire w_mdio, w_mdwe; |
// |
wire w_sd_cmd; |
wire [3:0] w_sd_data; |
fastmaster wbbus(i_clk, pwr_reset, |
// External USB-UART bus control |
rx_stb, rx_data, tx_stb, tx_data, tx_busy, |
// Board lights and switches |
i_sw, i_btn, o_led, |
o_clr_led0, o_clr_led1, o_clr_led2, o_clr_led3, |
// Board level PMod I/O |
i_aux_rx, o_aux_tx, o_aux_cts, i_gps_rx, o_gps_tx, |
// Quad SPI flash |
o_qspi_cs_n, w_qspi_sck, qspi_dat, io_qspi_dat, qspi_bmod, |
// DDR3 SDRAM |
o_ddr_reset_n, o_ddr_cke, |
o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n, |
w_ddr_dqs, o_ddr_addr, o_ddr_ba, wo_ddr_data, wi_ddr_data, |
// SD Card |
o_sd_sck, w_sd_cmd, w_sd_data, io_sd_cmd, io_sd, i_sd_cs, |
// Ethernet control (MDIO) lines |
o_eth_mdclk, w_mdio, w_mdwe, io_eth_mdio, |
// OLEDRGB PMod wires |
o_oled_sck, o_oled_cs_n, o_oled_mosi, o_oled_dcn, |
o_oled_reset_n, o_oled_vccen, o_oled_pmoden, |
// GPS PMod |
i_gps_pps, i_gps_3df |
); |
|
////// |
// |
// |
// Some wires need special treatment, and so are not quite completely |
// handled by the bus master. These are handled below. |
// |
// |
////// |
|
// |
// |
// QSPI)BMOD, Quad SPI bus mode, Bus modes are: |
// 0? Normal serial mode, one bit in one bit out |
// 10 Quad SPI mode, going out |
// 11 Quad SPI mode coming from the device (read mode) |
// |
// ?? Dual mode in (not yet) |
// ?? Dual mode out (not yet) |
// |
// |
assign io_qspi_dat = (~qspi_bmod[1])?({2'b11,1'bz,qspi_dat[0]}) |
:((qspi_bmod[0])?(4'bzzzz):(qspi_dat[3:0])); |
assign i_qspi_dat = io_qspi_dat; |
assign o_qspi_sck = w_qspi_sck; |
/* |
wire [3:0] i_qspi_dat_ign; |
ODDR #(.DDR_CLK_EDGE("OPPOSITE_EDGE"), .INIT(1'b1), .SRTYPE("SYNC")) |
qsck( |
.Q(o_qspi_sck), |
.C(i_clk), |
.CE(1'b1), |
.D1(w_qspi_sck), |
.D2(w_qspi_sck), |
.R(1'b0), .S(1'b0)); |
xioddr qd0(i_clk, (~qspi_bmod[1])|(~qspi_bmod[0]), |
{ qspi_dat[0], qspi_dat[0] }, |
{ i_qspi_dat[0], i_qspi_dat_ign[0] }, io_qspi_dat[0]); |
xioddr qd1(i_clk, (qspi_bmod == 2'b10), |
{ qspi_dat[1], qspi_dat[1] }, |
{ i_qspi_dat[1], i_qspi_dat_ign[1] }, io_qspi_dat[1]); |
xioddr qd2(i_clk, (~qspi_bmod[1])||(~qspi_bmod[0]), |
{ qspi_dat[2], qspi_dat[2] }, |
{ i_qspi_dat[2], i_qspi_dat_ign[2] }, io_qspi_dat[2]); |
xioddr qd3(i_clk, (~qspi_bmod[1])||(~qspi_bmod[0]), |
{ qspi_dat[3], qspi_dat[3] }, |
{ i_qspi_dat[3], i_qspi_dat_ign[3] }, io_qspi_dat[3]); |
*/ |
|
// |
// Proposed QSPI mode select, to allow dual I/O mode |
// 000 Normal SPI mode |
// 001 Dual mode input |
// 010 Dual mode, output |
// 101 Quad I/O mode input |
// 110 Quad I/O mode output |
// |
// |
// assign io_qspi_dat[3:2] = (~qspi_bmod[2]) ? 2'b11 |
// : (qspi_bmod[0])?2'bzz : qspi_dat[3:2]; |
// assign io_qspi_dat[1] = (~qspi_bmod[1])?qspi_dat[1]:1'bz; |
// assign io_qspi_dat[0] = (qspi_bmod[0])?1'bz : qspi_dat[0]; |
|
// |
// |
// The following primitive is necessary in order to gain access |
// to the o_qspi_sck pin. |
// |
// |
/* |
wire [3:0] su_nc; // Startup primitive, no connect |
STARTUPE2 #( |
// Leave PROG_USR false to avoid activating the program |
// event security feature. Notes state that such a feature |
// requires encrypted bitstreams. |
.PROG_USR("FALSE"), |
// Sets the configuration clock frequency (in ns) for |
// simulation. |
.SIM_CCLK_FREQ(0.0) |
) STARTUPE2_inst ( |
// CFGCLK, 1'b output: Configuration main clock output -- no connect |
.CFGCLK(su_nc[0]), |
// CFGMCLK, 1'b output: Configuration internal oscillator clock output |
.CFGMCLK(su_nc[1]), |
// EOS, 1'b output: Active high output indicating the End Of Startup. |
.EOS(su_nc[2]), |
// PREQ, 1'b output: PROGRAM request to fabric output |
// Only enabled if PROG_USR is set. This lets the fabric know |
// that a request has been made (either JTAG or pin pulled low) |
// to program the device |
.PREQ(su_nc[3]), |
// CLK, 1'b input: User start-up clock input |
.CLK(1'b0), |
// GSR, 1'b input: Global Set/Reset input |
.GSR(1'b0), |
// GTS, 1'b input: Global 3-state input |
.GTS(1'b0), |
// KEYCLEARB, 1'b input: Clear AES Decrypter Key input from BBRAM |
.KEYCLEARB(1'b0), |
// PACK, 1-bit input: PROGRAM acknowledge input |
// This pin is only enabled if PROG_USR is set. This allows the |
// FPGA to acknowledge a request for reprogram to allow the FPGA |
// to get itself into a reprogrammable state first. |
.PACK(1'b0), |
// USRCLKO, 1-bit input: User CCLK input -- This is why I am using this |
// module at all. |
.USRCCLKO(qspi_sck), |
// USRCCLKTS, 1'b input: User CCLK 3-state enable input |
// An active high here places the clock into a high impedence |
// state. Since we wish to use the clock as an active output |
// always, we drive this pin low. |
.USRCCLKTS(1'b0), |
// USRDONEO, 1'b input: User DONE pin output control |
// Set this to "high" to make sure that the DONE LED pin is |
// high. |
.USRDONEO(1'b1), |
// USRDONETS, 1'b input: User DONE 3-state enable output |
// This enables the FPGA DONE pin to be active. Setting this |
// active high sets the DONE pin to high impedence, setting it |
// low allows the output of this pin to be as stated above. |
.USRDONETS(1'b1) |
); |
*/ |
|
|
|
// |
// |
// Wires for setting up the SD Card Controller |
// |
// |
assign io_sd_cmd = w_sd_cmd ? 1'bz:1'b0; |
assign io_sd[0] = w_sd_data[0]? 1'bz:1'b0; |
assign io_sd[1] = w_sd_data[1]? 1'bz:1'b0; |
assign io_sd[2] = w_sd_data[2]? 1'bz:1'b0; |
assign io_sd[3] = w_sd_data[3]? 1'bz:1'b0; |
assign o_sd_wp = 1'b0; |
|
|
// |
// |
// Wire(s) for setting up the MDIO ethernet control structure |
// |
// |
assign io_eth_mdio = (w_mdwe)?w_mdio : 1'bz; |
|
// |
// |
// Wires for setting up the DDR3 memory |
// |
// |
wire [31:0] r_ddr_data; |
|
xioddr p0(i_clk, ~o_ddr_we_n, { wo_ddr_data[16], wo_ddr_data[0] }, |
{ wi_ddr_data[16], wi_ddr_data[0] }, io_ddr_data[0]); |
|
xioddr p1(i_clk, ~o_ddr_we_n, { wo_ddr_data[17], wo_ddr_data[1] }, |
{ wi_ddr_data[17], wi_ddr_data[1] }, io_ddr_data[1]); |
|
xioddr p2(i_clk, ~o_ddr_we_n, { wo_ddr_data[18], wo_ddr_data[2] }, |
{ wi_ddr_data[18], wi_ddr_data[2] }, io_ddr_data[2]); |
|
xioddr p3(i_clk, ~o_ddr_we_n, { wo_ddr_data[19], wo_ddr_data[3] }, |
{ wi_ddr_data[19], wi_ddr_data[3] }, io_ddr_data[3]); |
|
xioddr p4(i_clk, ~o_ddr_we_n, { wo_ddr_data[20], wo_ddr_data[4] }, |
{ wi_ddr_data[20], wi_ddr_data[4] }, io_ddr_data[4]); |
|
xioddr p5(i_clk, ~o_ddr_we_n, { wo_ddr_data[21], wo_ddr_data[5] }, |
{ wi_ddr_data[21], wi_ddr_data[5] }, io_ddr_data[5]); |
|
xioddr p6(i_clk, ~o_ddr_we_n, { wo_ddr_data[22], wo_ddr_data[6] }, |
{ wi_ddr_data[22], wi_ddr_data[6] }, io_ddr_data[6]); |
|
xioddr p7(i_clk, ~o_ddr_we_n, { wo_ddr_data[23], wo_ddr_data[7] }, |
{ wi_ddr_data[23], wi_ddr_data[7] }, io_ddr_data[7]); |
|
xioddr p8(i_clk, ~o_ddr_we_n, { wo_ddr_data[24], wo_ddr_data[8] }, |
{ wi_ddr_data[24], wi_ddr_data[8] }, io_ddr_data[8]); |
|
xioddr p9(i_clk, ~o_ddr_we_n, { wo_ddr_data[25], wo_ddr_data[9] }, |
{ wi_ddr_data[25], wi_ddr_data[9] }, io_ddr_data[9]); |
|
xioddr pa(i_clk, ~o_ddr_we_n, { wo_ddr_data[26], wo_ddr_data[10] }, |
{ wi_ddr_data[26], wi_ddr_data[10] }, io_ddr_data[10]); |
|
xioddr pb(i_clk, ~o_ddr_we_n, { wo_ddr_data[27], wo_ddr_data[11] }, |
{ wi_ddr_data[27], wi_ddr_data[11] }, io_ddr_data[11]); |
|
xioddr pc(i_clk, ~o_ddr_we_n, { wo_ddr_data[28], wo_ddr_data[12] }, |
{ wi_ddr_data[28], wi_ddr_data[12] }, io_ddr_data[12]); |
|
xioddr pd(i_clk, ~o_ddr_we_n, { wo_ddr_data[29], wo_ddr_data[13] }, |
{ wi_ddr_data[29], wi_ddr_data[13] }, io_ddr_data[13]); |
|
xioddr pe(i_clk, ~o_ddr_we_n, { wo_ddr_data[30], wo_ddr_data[14] }, |
{ wi_ddr_data[30], wi_ddr_data[14] }, io_ddr_data[14]); |
|
xioddr pf(i_clk, ~o_ddr_we_n, { wo_ddr_data[31], wo_ddr_data[15] }, |
{ wi_ddr_data[31], wi_ddr_data[15] }, io_ddr_data[15]); |
|
OBUFTDS #(.IOSTANDARD("DIFF_SSTL135"), .SLEW("FAST")) |
dqsbuf0(.O(io_ddr_dqs_p[0]), .OB(io_ddr_dqs_n[0]), |
.I(w_ddr_dqs[1]), .T(w_ddr_dqs[2])); |
OBUFTDS #(.IOSTANDARD("DIFF_SSTL135"), .SLEW("FAST")) |
dqsbuf1(.O(io_ddr_dqs_p[1]), .OB(io_ddr_dqs_n[1]), |
.I(w_ddr_dqs[0]), .T(w_ddr_dqs[2])); |
|
OBUFDS #(.IOSTANDARD("DIFF_SSTL135"), .SLEW("FAST")) |
clkbuf(.O(o_ddr_ck_p), .OB(o_ddr_ck_n), .I(clk_for_ddr)); |
|
assign o_ddr_dm = 2'b00; |
assign o_ddr_odt = 1'b0; |
|
endmodule |
|
/rtl/enetctrl.v
0,0 → 1,209
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: enetctrl |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: This module translates wishbone commands, whether they be read |
// or write commands, to MIO commands operating on an Ethernet |
// controller, such as the TI DP83848 controller on the Artix-7 Arty |
// development boarod (used by this project). As designed, the bus |
// *will* stall until the command has been completed. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`define ECTRL_RESET 3'h0 |
`define ECTRL_IDLE 3'h1 |
`define ECTRL_ADDRESS 3'h2 |
`define ECTRL_READ 3'h3 |
`define ECTRL_WRITE 3'h4 |
module enetctrl(i_clk, i_rst, |
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_mdclk, o_mdio, i_mdio, o_mdwe); |
parameter CLKBITS=3; // = 3 for 200MHz source clock, 2 for 100 MHz |
input i_clk, i_rst; |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [4:0] i_wb_addr; |
input [15:0] i_wb_data; |
output reg o_wb_ack, o_wb_stall; |
output wire [31:0] o_wb_data; |
// |
input i_mdio; |
output wire o_mdclk; |
output reg o_mdio, o_mdwe; |
// |
parameter PHYADDR = 5'h01; |
|
|
reg read_pending, write_pending; |
reg [4:0] r_addr; |
reg [15:0] read_reg, write_reg, r_data; |
reg [2:0] ctrl_state; |
reg [5:0] reg_pos; |
reg zreg_pos; |
reg [15:0] r_wb_data; |
|
|
// Step 1: Generate our clock |
reg [(CLKBITS-1):0] clk_counter; |
initial clk_counter = 0; |
always @(posedge i_clk) |
clk_counter <= clk_counter + 1; |
assign o_mdclk = clk_counter[(CLKBITS-1)]; |
|
// Step 2: Generate strobes for when to move, given the clock |
reg rclk, zclk; |
initial zclk = 0; |
always @(posedge i_clk) |
zclk <= &clk_counter; |
initial rclk = 0; |
always @(posedge i_clk) |
rclk <= (~clk_counter[(CLKBITS-1)])&&(&clk_counter[(CLKBITS-2):0]); |
|
// Step 3: Read from our input port |
// Note: I read on the falling edge, he changes on the rising edge |
always @(posedge i_clk) |
if (zclk) |
read_reg <= { read_reg[14:0], i_mdio }; |
always @(posedge i_clk) |
zreg_pos <= (reg_pos == 0); |
|
always @(posedge i_clk) |
if (rclk) |
r_wb_data <= read_reg; |
assign o_wb_data = { 16'h00, r_wb_data }; |
|
// Step 4: Write to our output port |
// Note: I change on the falling edge, |
always @(posedge i_clk) |
if (zclk) |
o_mdio <= write_reg[15]; |
|
reg in_idle; |
initial in_idle = 1'b0; |
always @(posedge i_clk) |
in_idle <= (ctrl_state == `ECTRL_IDLE); |
initial o_wb_stall = 1'b0; |
always @(posedge i_clk) |
if (ctrl_state != `ECTRL_IDLE) |
o_wb_stall <= 1'b1; |
else if (o_wb_ack) |
o_wb_stall <= 1'b0; |
else if (((i_wb_stb)&&(in_idle))||(read_pending)||(write_pending)) |
o_wb_stall <= 1'b1; |
else o_wb_stall <= 1'b0; |
|
initial read_pending = 1'b0; |
initial write_pending = 1'b0; |
always @(posedge i_clk) |
begin |
r_addr <= i_wb_addr; |
if ((i_wb_stb)&&(~o_wb_stall)) |
r_data <= i_wb_data; |
if ((i_rst)||(ctrl_state == `ECTRL_READ)||(ctrl_state == `ECTRL_WRITE)) |
begin |
read_pending <= 1'b0; |
write_pending <= 1'b0; |
end else if ((i_wb_stb)&&(~o_wb_stall)) |
begin |
read_pending <= (~i_wb_we); |
write_pending <= (i_wb_we); |
end |
end |
|
initial reg_pos = 6'h3f; |
initial ctrl_state = `ECTRL_RESET; |
initial write_reg = 16'hffff; |
always @(posedge i_clk) |
begin |
o_wb_ack <= 1'b0; |
if ((zclk)&&(~zreg_pos)) |
reg_pos <= reg_pos - 1; |
if (zclk) |
write_reg <= { write_reg[14:0], 1'b1 }; |
if (i_rst) |
begin // Must go for 167 ms before our 32 clocks |
ctrl_state <= `ECTRL_RESET; |
reg_pos <= 6'h3f; |
write_reg[15:0] <= 16'hffff; |
end else case(ctrl_state) |
`ECTRL_RESET: begin |
o_mdwe <= 1'b1; // Write |
write_reg[15:0] <= 16'hffff; |
if ((zclk)&&(zreg_pos)) |
ctrl_state <= `ECTRL_IDLE; |
end |
`ECTRL_IDLE: begin |
o_mdwe <= 1'b1; // Write |
write_reg <= { 4'he, PHYADDR, r_addr, 2'b11 }; |
if (write_pending) |
write_reg[15:12] <= { 4'h5 }; |
else if (read_pending) |
write_reg[15:12] <= { 4'h6 }; |
if (read_pending || write_pending) |
begin |
reg_pos <= 6'h0f; |
ctrl_state <= `ECTRL_ADDRESS; |
end end |
`ECTRL_ADDRESS: begin |
o_mdwe <= 1'b1; // Write |
if ((zreg_pos)&&(zclk)) |
begin |
reg_pos <= 6'h10; |
if (read_pending) |
ctrl_state <= `ECTRL_READ; |
else |
ctrl_state <= `ECTRL_WRITE; |
write_reg <= r_data; |
end end |
`ECTRL_READ: begin |
o_mdwe <= 1'b0; // Read |
if ((zreg_pos)&&(zclk)) |
begin |
ctrl_state <= `ECTRL_IDLE; |
o_wb_ack <= 1'b1; |
end end |
`ECTRL_WRITE: begin |
o_mdwe <= 1'b1; // Write |
if ((zreg_pos)&&(zclk)) |
begin |
ctrl_state <= `ECTRL_IDLE; |
o_wb_ack <= 1'b1; |
end end |
default: begin |
o_mdwe <= 1'b0; // Read |
reg_pos <= 6'h3f; |
ctrl_state <= `ECTRL_RESET; |
end |
endcase |
end |
|
endmodule |
/rtl/flash_config.v
0,0 → 1,48
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: flashconfig.v |
// |
// Project: Wishbone Controlled Quad SPI Flash Controller |
// |
// Purpose: A configuration file, separated from the controller file, so |
// that multiple files can use the same wishbone Quad Spi Flash |
// controller, while each having a separate configuration. Currently, |
// the configuration only includes whether the flash is read only or not. |
// Other configuration options may be added later. |
// |
// |
// Creator: Dan Gisselquist |
// 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
`ifndef FLASH_CONFIG_V |
`define FLASH_CONFIG_V |
// |
// `define READ_ONLY |
// |
`endif |
// |
/rtl/wbureadcw.v
0,0 → 1,126
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbureadcw.v |
// |
// Project: FPGA library |
// |
// Purpose: Read bytes from a serial port (i.e. the jtagser) and translate |
// those bytes into a 6-byte codeword. This codeword may specify |
// a number of values to be read, the value to be written, or an address |
// to read/write from, or perhaps the end of a write sequence. |
// |
// See the encoding documentation file for more information. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// |
// Goal: single clock pipeline, 50 slices or less |
// |
module wbureadcw(i_clk, i_stb, i_valid, i_hexbits, |
o_stb, o_codword); |
input i_clk, i_stb, i_valid; |
input [5:0] i_hexbits; |
output reg o_stb; |
output reg [35:0] o_codword; |
|
|
// Timing: |
// Clock 0: i_stb is high, i_valid is low |
// Clock 1: shiftreg[5:0] is valid, cw_len is valid |
// r_len = 1 |
// Clock 2: o_stb = 1, for cw_len = 1; |
// o_codword is 1-byte valid |
// i_stb may go high again on this clock as well. |
// Clock 3: o_stb = 0 (cw_len=1), |
// cw_len = 0, |
// r_len = 0 (unless i_stb) |
// Ready for next word |
|
reg [2:0] r_len, cw_len; |
reg [1:0] lastcw; |
|
wire w_stb; |
assign w_stb = ((r_len == cw_len)&&(cw_len != 0)) |
||((i_stb)&&(~i_valid)&&(lastcw == 2'b01)); |
|
// r_len is the length of the codeword as it exists |
// in our register |
initial r_len = 3'h0; |
always @(posedge i_clk) |
if ((i_stb)&&(~i_valid)) // Newline reset |
r_len <= 0; |
else if (w_stb) // reset/restart w/o newline |
r_len <= (i_stb)? 3'h1:3'h0; |
else if (i_stb) //in middle of word |
r_len <= r_len + 3'h1; |
|
reg [35:0] shiftreg; |
always @(posedge i_clk) |
if (w_stb) |
shiftreg[35:30] <= i_hexbits; |
else if (i_stb) case(r_len) |
3'b000: shiftreg[35:30] <= i_hexbits; |
3'b001: shiftreg[29:24] <= i_hexbits; |
3'b010: shiftreg[23:18] <= i_hexbits; |
3'b011: shiftreg[17:12] <= i_hexbits; |
3'b100: shiftreg[11: 6] <= i_hexbits; |
3'b101: shiftreg[ 5: 0] <= i_hexbits; |
default: begin end |
endcase |
|
always @(posedge i_clk) |
if (o_stb) |
lastcw <= o_codword[35:34]; |
always @(posedge i_clk) |
if ((i_stb)&&(~i_valid)&&(lastcw == 2'b01)) |
o_codword[35:30] <= 6'h2e; |
else |
o_codword <= shiftreg; |
|
// How long do we expect this codeword to be? |
initial cw_len = 3'b000; |
always @(posedge i_clk) |
if ((i_stb)&&(~i_valid)) |
cw_len <= 0; |
else if ((i_stb)&&((cw_len == 0)||(w_stb))) |
begin |
if (i_hexbits[5:4] == 2'b11) // 2b vector read |
cw_len <= 3'h2; |
else if (i_hexbits[5:4] == 2'b10) // 1b vector read |
cw_len <= 3'h1; |
else if (i_hexbits[5:3] == 3'b010) // 2b compressed wr |
cw_len <= 3'h2; |
else if (i_hexbits[5:3] == 3'b001) // 2b compressed addr |
cw_len <= 3'b010 + { 1'b0, i_hexbits[2:1] }; |
else // long write or set address |
cw_len <= 3'h6; |
end else if (w_stb) |
cw_len <= 0; |
|
always @(posedge i_clk) |
o_stb <= w_stb; |
|
endmodule |
|
/rtl/wbqspiflash.v
0,0 → 1,1202
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbspiflash.v |
// |
// Project: Wishbone Controlled Quad SPI Flash Controller |
// |
// Purpose: Access a Quad SPI flash via a WISHBONE interface. This |
// includes both read and write (and erase) commands to the SPI |
// flash. All read/write commands are accomplished using the |
// high speed (4-bit) interface. Further, the device will be |
// left/kept in the 4-bit read interface mode between accesses, |
// for a minimum read latency. |
// |
// Wishbone Registers (See spec sheet for more detail): |
// 0: local config(r) / erase commands(w) / deep power down cmds / etc. |
// R: (Write in Progress), (dirty-block), (spi_port_busy), 1'b0, 9'h00, |
// { last_erased_sector, 14'h00 } if (WIP) |
// else { current_sector_being_erased, 14'h00 } |
// current if write in progress, last if written |
// W: (1'b1 to erase), (12'h ignored), next_erased_block, 14'h ignored) |
// 1: Configuration register |
// 2: Status register (R/w) |
// 3: Read ID (read only) |
// (19 bits): Data (R/w, but expect writes to take a while) |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
`include "flash_config.v" |
// |
`define WBQSPI_RESET 0 |
`define WBQSPI_RESET_QUADMODE 1 |
`define WBQSPI_IDLE 2 |
`define WBQSPI_RDIDLE 3 // Idle, but in fast read mode |
`define WBQSPI_WBDECODE 4 |
`define WBQSPI_RD_DUMMY 5 |
`define WBQSPI_QRD_ADDRESS 6 |
`define WBQSPI_QRD_DUMMY 7 |
`define WBQSPI_READ_CMD 8 |
`define WBQSPI_READ_DATA 9 |
`define WBQSPI_WAIT_TIL_RDIDLE 10 |
`define WBQSPI_READ_ID_CMD 11 |
`define WBQSPI_READ_ID 12 |
`define WBQSPI_READ_STATUS 13 |
`define WBQSPI_READ_CONFIG 14 |
`define WBQSPI_WAIT_TIL_IDLE 15 |
// |
// |
`ifndef READ_ONLY |
// |
`define WBQSPI_WAIT_WIP_CLEAR 16 |
`define WBQSPI_CHECK_WIP_CLEAR 17 |
`define WBQSPI_CHECK_WIP_DONE 18 |
`define WBQSPI_WEN 19 |
`define WBQSPI_PP 20 // Program page |
`define WBQSPI_QPP 21 // Program page, 4 bit mode |
`define WBQSPI_WR_DATA 22 |
`define WBQSPI_WR_BUS_CYCLE 23 |
`define WBQSPI_WRITE_STATUS 24 |
`define WBQSPI_WRITE_CONFIG 25 |
`define WBQSPI_ERASE_WEN 26 |
`define WBQSPI_ERASE_CMD 27 |
`define WBQSPI_ERASE_BLOCK 28 |
`define WBQSPI_CLEAR_STATUS 29 |
`define WBQSPI_IDLE_CHECK_WIP 30 |
// |
`endif |
|
module wbqspiflash(i_clk, |
// Internal wishbone connections |
i_wb_cyc, i_wb_data_stb, i_wb_ctrl_stb, i_wb_we, |
i_wb_addr, i_wb_data, |
// Wishbone return values |
o_wb_ack, o_wb_stall, o_wb_data, |
// Quad Spi connections to the external device |
o_qspi_sck, o_qspi_cs_n, o_qspi_mod, o_qspi_dat, i_qspi_dat, |
o_interrupt); |
parameter ADDRESS_WIDTH=22; |
input i_clk; |
// Wishbone, inputs first |
input i_wb_cyc, i_wb_data_stb, i_wb_ctrl_stb, i_wb_we; |
input [(ADDRESS_WIDTH-3):0] i_wb_addr; |
input [31:0] i_wb_data; |
// then outputs |
output reg o_wb_ack; |
output reg o_wb_stall; |
output reg [31:0] o_wb_data; |
// Quad SPI control wires |
output wire o_qspi_sck, o_qspi_cs_n; |
output wire [1:0] o_qspi_mod; |
output wire [3:0] o_qspi_dat; |
input [3:0] i_qspi_dat; |
// Interrupt line |
output reg o_interrupt; |
// output wire [31:0] o_debug; |
|
reg spi_wr, spi_hold, spi_spd, spi_dir; |
reg [31:0] spi_in; |
reg [1:0] spi_len; |
wire [31:0] spi_out; |
wire spi_valid, spi_busy; |
wire w_qspi_sck, w_qspi_cs_n; |
wire [3:0] w_qspi_dat; |
wire [1:0] w_qspi_mod; |
// wire [22:0] spi_dbg; |
llqspi lldriver(i_clk, |
spi_wr, spi_hold, spi_in, spi_len, spi_spd, spi_dir, |
spi_out, spi_valid, spi_busy, |
w_qspi_sck, w_qspi_cs_n, w_qspi_mod, w_qspi_dat, |
i_qspi_dat); |
|
// Erase status tracking |
reg write_in_progress, write_protect; |
reg [(ADDRESS_WIDTH-17):0] erased_sector; |
reg dirty_sector; |
initial begin |
write_in_progress = 1'b0; |
erased_sector = 0; |
dirty_sector = 1'b1; |
write_protect = 1'b1; |
end |
|
reg qspi_full_stop; |
always @(posedge i_clk) |
qspi_full_stop <= ((~spi_busy)&&(o_qspi_cs_n)&&(~spi_wr)); |
|
reg [7:0] last_status; |
reg quad_mode_enabled; |
reg spif_cmd, spif_override, wr_same_row, pipeline_read; |
reg [(ADDRESS_WIDTH-3):0] spif_addr; |
reg [31:0] spif_data; |
reg [5:0] state; |
reg spif_ctrl, spif_req; |
wire [(ADDRESS_WIDTH-17):0] spif_sector; |
assign spif_sector = spif_addr[(ADDRESS_WIDTH-3):14]; |
|
// assign o_debug = { spi_wr, spi_spd, spi_hold, state, spi_dbg }; |
|
initial state = `WBQSPI_RESET; |
initial o_wb_ack = 1'b0; |
initial o_wb_stall = 1'b1; |
initial spi_wr = 1'b0; |
initial spi_len = 2'b00; |
initial quad_mode_enabled = 1'b0; |
initial o_interrupt = 1'b0; |
always @(posedge i_clk) |
begin |
spif_override <= 1'b0; |
if (state == `WBQSPI_RESET) |
begin |
// From a reset, we should |
// Enable the Quad I/O mode |
// Disable the Write protection bits in the status register |
// Chip should already be up and running, so we can start |
// immediately .... |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
spi_wr <= 1'b0; |
spi_hold <= 1'b0; |
spi_spd <= 1'b0; |
spi_dir <= 1'b0; |
last_status <= 8'h00; |
state <= `WBQSPI_RESET_QUADMODE; |
spif_req <= 1'b0; |
spif_override <= 1'b1; |
last_status <= 8'hfc; // |
// This guarantees that we aren't starting in quad |
// I/O mode, where the FPGA configuration scripts may |
// have left us. |
end else if (state == `WBQSPI_RESET_QUADMODE) |
begin |
// Okay, so here's the problem: we don't know whether or not |
// the Xilinx loader started us up in Quad Read I/O idle mode. |
// So, thus we need to |
// Not ready to handle the bus yet, so stall any requests |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
|
// Do something ... |
if (last_status == 8'h00) |
begin |
spif_override <= 1'b0; |
state <= `WBQSPI_IDLE; |
end else begin |
last_status <= last_status - 8'h1; |
spif_override <= 1'b1; |
spif_cmd <= last_status[3]; // Toggle CS_n |
spif_ctrl <= last_status[0]; // Toggle clock too |
end |
end else if (state == `WBQSPI_IDLE) |
begin |
o_interrupt <= 1'b0; |
o_wb_stall <= 1'b0; |
o_wb_ack <= 1'b0; |
spif_cmd <= i_wb_we; |
spif_addr <= i_wb_addr; |
spif_data <= i_wb_data; |
spif_ctrl <= (i_wb_ctrl_stb)&&(~i_wb_data_stb); |
spif_req <= (i_wb_ctrl_stb)||(i_wb_data_stb); |
spi_wr <= 1'b0; // Keep the port idle, unless told otherwise |
spi_hold <= 1'b0; |
spi_spd <= 1'b0; |
spi_dir <= 1'b0; // Write (for now, 'cause of cmd) |
// |
if ((i_wb_data_stb)&&(~i_wb_we)&&(~write_in_progress)) |
begin // Read access, normal mode(s) |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
spi_wr <= 1'b1; // Write cmd to device |
spi_len <= 2'b11; |
if (quad_mode_enabled) |
begin |
// spi_in <= { 8'heb, bus_address }; |
state <= `WBQSPI_QRD_ADDRESS; |
end else begin |
//spi_in <= { 8'h0b, bus_address }; |
state <= `WBQSPI_RD_DUMMY; |
end |
end else if((i_wb_ctrl_stb)&&(~i_wb_we)&&(i_wb_addr[1:0] == 2'b00)) |
begin |
// A local read that doesn't touch the device, so leave |
// the device in its current state |
o_wb_stall <= 1'b0; |
o_wb_ack <= 1'b1; |
o_wb_data <= { write_in_progress, |
dirty_sector, spi_busy, |
~write_protect, |
quad_mode_enabled, |
{(29-ADDRESS_WIDTH){1'b0}}, |
erased_sector, 14'h000 }; |
end else if ((i_wb_ctrl_stb)||(i_wb_data_stb)) |
begin // Need to release the device from quad mode for all else |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
spi_wr <= 1'b0; |
spi_len <= 2'b11; |
// spi_in <= 32'h00; |
state <= `WBQSPI_WBDECODE; |
end |
end else if (state == `WBQSPI_RDIDLE) |
begin |
spi_wr <= 1'b0; |
o_wb_stall <= 1'b0; |
o_wb_ack <= 1'b0; |
spif_cmd <= i_wb_we; |
spif_addr <= i_wb_addr; |
spif_data <= i_wb_data; |
spif_ctrl <= (i_wb_ctrl_stb)&&(~i_wb_data_stb); |
spif_req <= (i_wb_ctrl_stb)||(i_wb_data_stb); |
spi_hold <= 1'b0; |
spi_spd<= 1'b1; |
spi_dir <= 1'b0; // Write (for now) |
if ((i_wb_data_stb)&&(~i_wb_we)) |
begin // Continue our read ... send the new address / mode |
o_wb_stall <= 1'b1; |
spi_wr <= 1'b1; |
spi_len <= 2'b10; // Write address, but not mode byte |
// spi_in <= { bus_address, 8'ha0 }; |
state <= `WBQSPI_QRD_DUMMY; |
end else if((i_wb_ctrl_stb)&&(~i_wb_we)&&(i_wb_addr[1:0] == 2'b00)) |
begin |
// A local read that doesn't touch the device, so leave |
// the device in its current state |
o_wb_stall <= 1'b0; |
o_wb_ack <= 1'b1; |
o_wb_data <= { write_in_progress, |
dirty_sector, spi_busy, |
~write_protect, |
quad_mode_enabled, |
{(29-ADDRESS_WIDTH){1'b0}}, |
erased_sector, 14'h000 }; |
end else if ((i_wb_ctrl_stb)||(i_wb_data_stb)) |
begin // Need to release the device from quad mode for all else |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
spi_wr <= 1'b1; |
spi_len <= 2'b11; |
// spi_in <= 32'h00; |
state <= `WBQSPI_WBDECODE; |
end |
end else if (state == `WBQSPI_WBDECODE) |
begin |
// We were in quad SPI read mode, and had to get out. |
// Now we've got a command (not data read) to read and |
// execute. Accomplish what we would've done while in the |
// IDLE state here, save only that we don't have to worry |
// about data reads, and we need to operate on a stored |
// version of the bus command |
o_wb_stall <= 1'b1; |
o_wb_ack <= 1'b0; |
spi_wr <= 1'b0; // Keep the port idle, unless told otherwise |
spi_hold <= 1'b0; |
spi_spd <= 1'b0; |
spi_dir <= 1'b0; |
spif_req<= (spif_req) && (i_wb_cyc); |
if (qspi_full_stop) // only in full idle ... |
begin |
// Data register access |
if (~spif_ctrl) |
begin |
if (spif_cmd) // Request to write a page |
begin |
`ifdef READ_ONLY |
o_wb_ack <= spif_req; |
o_wb_stall <= 1'b0; |
state <= `WBQSPI_IDLE; |
`else |
if((~write_protect)&&(~write_in_progress)) |
begin // 00 |
spi_wr <= 1'b1; |
spi_len <= 2'b00; // 8 bits |
// Send a write enable command |
// spi_in <= { 8'h06, 24'h00 }; |
state <= `WBQSPI_WEN; |
|
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
end else if (write_protect) |
begin // whether or not write-in_progress ... |
// Do nothing on a write protect |
// violation |
// |
o_wb_ack <= spif_req; |
o_wb_stall <= 1'b0; |
state <= `WBQSPI_IDLE; |
end else begin // write is in progress, wait |
// for it to complete |
state <= `WBQSPI_WAIT_WIP_CLEAR; |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
end |
// end else if (~write_in_progress) // always true |
// but ... we wouldn't get here on a normal read access |
`endif |
end else begin |
// Something's wrong, we should never |
// get here |
// Attempt to go to idle to recover |
state <= `WBQSPI_IDLE; |
end |
end else if ((spif_ctrl)&&(spif_cmd)) |
begin |
`ifdef READ_ONLY |
o_wb_ack <= spif_req; |
o_wb_stall <= 1'b0; |
state <= `WBQSPI_IDLE; |
`else |
o_wb_stall <= 1'b1; |
case(spif_addr[1:0]) |
2'b00: begin // Erase command register |
o_wb_ack <= spif_req; |
o_wb_stall <= 1'b0; |
state <= `WBQSPI_IDLE; |
write_protect <= ~spif_data[28]; |
// Are we commanding an erase? |
// We're in read mode, writes cannot |
// be in progress, so ... |
if (spif_data[31]) // Command an erase |
begin |
// Since we're not going back |
// to IDLE, we must stall the |
// bus here |
o_wb_stall <= 1'b1; |
spi_wr <= 1'b1; |
spi_len <= 2'b00; |
// Send a write enable command |
// spi_in <= { 8'h06, 24'h00 }; |
state <= `WBQSPI_ERASE_CMD; |
end end |
2'b01: begin |
// Write the configuration register |
o_wb_ack <= spif_req; |
o_wb_stall <= 1'b1; |
|
// Need to send a write enable command first |
spi_wr <= 1'b1; |
spi_len <= 2'b00; // 8 bits |
// Send a write enable command |
// spi_in <= { 8'h06, 24'h00 }; |
state <= `WBQSPI_WRITE_CONFIG; |
end |
2'b10: begin |
// Write the status register |
o_wb_ack <= spif_req; // Ack immediately |
o_wb_stall <= 1'b1; // Stall other cmds |
// Need to send a write enable command first |
spi_wr <= 1'b1; |
spi_len <= 2'b00; // 8 bits |
// Send a write enable command |
// spi_in <= { 8'h06, 24'h00 }; |
state <= `WBQSPI_WRITE_STATUS; |
end |
2'b11: begin // Write the ID register??? makes no sense |
o_wb_ack <= spif_req; |
o_wb_stall <= 1'b0; |
state <= `WBQSPI_IDLE; |
end |
endcase |
`endif |
end else begin // on (~spif_we) |
case(spif_addr[1:0]) |
2'b00: begin // Read local register |
// Nonsense case--would've done this |
// already |
state <= `WBQSPI_IDLE; |
o_wb_ack <= spif_req; |
o_wb_stall <= 1'b0; |
end |
2'b01: begin // Read configuration register |
state <= `WBQSPI_READ_CONFIG; |
spi_wr <= 1'b1; |
spi_len <= 2'b01; |
// spi_in <= { 8'h35, 24'h00}; |
|
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
end |
2'b10: begin // Read status register |
state <= `WBQSPI_READ_STATUS; |
spi_wr <= 1'b1; |
spi_len <= 2'b01; // 8 bits out, 8 bits in |
// spi_in <= { 8'h05, 24'h00}; |
|
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
end |
2'b11: begin // Read ID register |
state <= `WBQSPI_READ_ID_CMD; |
spi_wr <= 1'b1; |
spi_len <= 2'b00; |
// spi_in <= { 8'h9f, 24'h00}; |
|
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
end |
endcase |
end |
end |
// |
// |
// READ DATA section: for both data and commands |
// |
end else if (state == `WBQSPI_RD_DUMMY) |
begin |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
|
spi_wr <= 1'b1; // Non-stop |
// Need to read one byte of dummy data, |
// just to consume 8 clocks |
// spi_in <= { 8'h00, 24'h00 }; |
spi_len <= 2'b00; // Read 8 bits |
spi_spd <= 1'b0; |
spi_hold <= 1'b0; |
spif_req<= (spif_req) && (i_wb_cyc); |
|
if ((~spi_busy)&&(~o_qspi_cs_n)) |
// Our command was accepted |
state <= `WBQSPI_READ_CMD; |
end else if (state == `WBQSPI_QRD_ADDRESS) |
begin |
// We come in here immediately upon issuing a QRD read |
// command (8-bits), but we have to pause to give the |
// address (24-bits) and mode (8-bits) in quad speed. |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
|
spi_wr <= 1'b1; // Non-stop |
// spi_in <= { {(24-ADDRESS_WIDTH){1'b0}}, |
// spif_addr[(ADDRESS_WIDTH-3):0], 2'b00, 8'ha0 }; |
spi_len <= 2'b10; // Write address, not mode byte |
spi_spd <= 1'b1; |
spi_dir <= 1'b0; // Still writing |
spi_hold <= 1'b0; |
spif_req<= (spif_req) && (i_wb_cyc); |
|
if ((~spi_busy)&&(spi_spd)) |
// Our command was accepted |
state <= `WBQSPI_QRD_DUMMY; |
end else if (state == `WBQSPI_QRD_DUMMY) |
begin |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
|
spi_wr <= 1'b1; // Non-stop |
// spi_in <= { 8'ha0, 24'h00 }; // Mode byte, then 2 bytes dummy |
spi_len <= 2'b10; // Write 8 bits |
spi_spd <= 1'b1; |
spi_dir <= 1'b0; // Still writing |
spi_hold <= 1'b0; |
spif_req<= (spif_req) && (i_wb_cyc); |
|
if ((~spi_busy)&&(spi_in[31:28] == 4'ha)) |
// Our command was accepted |
state <= `WBQSPI_READ_CMD; |
end else if (state == `WBQSPI_READ_CMD) |
begin // Issue our first command to read 32 bits. |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
|
spi_wr <= 1'b1; |
// spi_in <= { 8'hff, 24'h00 }; // Empty |
spi_len <= 2'b11; // Read 32 bits |
spi_dir <= 1'b1; // Now reading |
spi_hold <= 1'b0; |
spif_req<= (spif_req) && (i_wb_cyc); |
if ((spi_valid)&&(spi_len == 2'b11)) |
state <= `WBQSPI_READ_DATA; |
end else if (state == `WBQSPI_READ_DATA) |
begin |
// Pipelined read support |
spi_wr <=pipeline_read; |
// spi_in <= 32'h00; |
spi_len <= 2'b11; |
// Don't adjust the speed here, it was set in the setup |
spi_dir <= 1'b1; // Now we get to read |
// Don't let the device go to idle until the bus cycle ends. |
// This actually prevents a *really* nasty race condition, |
// where the strobe comes in after the lower level device |
// has decided to stop waiting. The write is then issued, |
// but no one is listening. By leaving the device open, |
// the device is kept in a state where a valid strobe |
// here will be useful. Of course, we don't accept |
// all commands, just reads. Further, the strobe needs |
// to be high for two clocks cycles without changing |
// anything on the bus--one for us to notice it and pull |
// our head out of the sand, and a second for whoever |
// owns the bus to realize their command went through. |
spi_hold <= 1'b1; |
spif_req<= (spif_req) && (i_wb_cyc); |
if ((spi_valid)&&(~spi_in[31])) |
begin // Single pulse acknowledge and write data out |
o_wb_ack <= spif_req; |
o_wb_stall <= (~spi_wr); |
// adjust endian-ness to match the PC |
o_wb_data <= { spi_out[7:0], spi_out[15:8], |
spi_out[23:16], spi_out[31:24] }; |
state <= (spi_wr)?`WBQSPI_READ_DATA |
: ((spi_spd) ? `WBQSPI_WAIT_TIL_RDIDLE : `WBQSPI_WAIT_TIL_IDLE); |
spif_req <= spi_wr; |
spi_hold <= (~spi_wr); |
if (spi_wr) |
spif_addr <= i_wb_addr; |
end else if (~i_wb_cyc) |
begin // FAIL SAFE: If the bus cycle ends, forget why we're |
// here, just go back to idle |
state <= ((spi_spd) ? `WBQSPI_WAIT_TIL_RDIDLE : `WBQSPI_WAIT_TIL_IDLE); |
spi_hold <= 1'b0; |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
end else begin |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
end |
end else if (state == `WBQSPI_WAIT_TIL_RDIDLE) |
begin // Wait 'til idle, but then go to fast read idle instead of full |
spi_wr <= 1'b0; // idle |
spi_hold <= 1'b0; |
o_wb_stall <= 1'b1; |
o_wb_ack <= 1'b0; |
spif_req <= 1'b0; |
if (qspi_full_stop) // Wait for a full |
begin // clearing of the SPI port before moving on |
state <= `WBQSPI_RDIDLE; |
o_wb_stall <= 1'b0; |
o_wb_ack <= 1'b0;// Shouldn't be acking anything here |
end |
end else if (state == `WBQSPI_READ_ID_CMD) |
begin // We came into here immediately after issuing a 0x9f command |
// Now we need to read 32 bits of data. Result should be |
// 0x0102154d (8'h manufacture ID, 16'h device ID, followed |
// by the number of extended bytes available 8'h4d). |
o_wb_ack <= 1'b0; |
o_wb_stall<= 1'b1; |
|
spi_wr <= 1'b1; // No data to send, but need four bytes, since |
spi_len <= 2'b11; // 32 bits of data are ... useful |
// spi_in <= 32'h00; // Irrelevant |
spi_spd <= 1'b0; // Slow speed |
spi_dir <= 1'b1; // Reading |
spi_hold <= 1'b0; |
spif_req <= (spif_req) && (i_wb_cyc); |
if ((~spi_busy)&&(~o_qspi_cs_n)&&(spi_len == 2'b11)) |
// Our command was accepted, now go read the result |
state <= `WBQSPI_READ_ID; |
end else if (state == `WBQSPI_READ_ID) |
begin |
o_wb_ack <= 1'b0; // Assuming we're still waiting |
o_wb_stall <= 1'b1; |
|
spi_wr <= 1'b0; // No more writes, we've already written the cmd |
spi_hold <= 1'b0; |
spif_req <= (spif_req) && (i_wb_cyc); |
|
// Here, we just wait until the result comes back |
// The problem is, the result may be the previous result. |
// So we use spi_len as an indicator |
spi_len <= 2'b00; |
if((spi_valid)&&(spi_len==2'b00)) |
begin // Put the results out as soon as possible |
o_wb_data <= spi_out[31:0]; |
o_wb_ack <= spif_req; |
spif_req <= 1'b0; |
end else if ((~spi_busy)&&(o_qspi_cs_n)) |
begin |
state <= `WBQSPI_IDLE; |
o_wb_stall <= 1'b0; |
end |
end else if (state == `WBQSPI_READ_STATUS) |
begin // We enter after the command has been given, for now just |
// read and return |
spi_wr <= 1'b0; |
o_wb_ack <= 1'b0; |
spi_hold <= 1'b0; |
spif_req <= (spif_req) && (i_wb_cyc); |
if (spi_valid) |
begin |
o_wb_ack <= spif_req; |
o_wb_stall <= 1'b1; |
spif_req <= 1'b0; |
last_status <= spi_out[7:0]; |
write_in_progress <= spi_out[0]; |
if (spif_addr[1:0] == 2'b00) // Local read, checking |
begin // status, 'cause we're writing |
o_wb_data <= { spi_out[0], |
dirty_sector, spi_busy, |
~write_protect, |
quad_mode_enabled, |
{(29-ADDRESS_WIDTH){1'b0}}, |
erased_sector, 14'h000 }; |
end else begin |
o_wb_data <= { 24'h00, spi_out[7:0] }; |
end |
end |
|
if (qspi_full_stop) |
state <= `WBQSPI_IDLE; |
end else if (state == `WBQSPI_READ_CONFIG) |
begin // We enter after the command has been given, for now just |
// read and return |
spi_wr <= 1'b0; |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
spi_hold <= 1'b0; |
spif_req <= (spif_req) && (i_wb_cyc); |
|
if (spi_valid) |
begin |
o_wb_data <= { 24'h00, spi_out[7:0] }; |
quad_mode_enabled <= spi_out[1]; |
end |
|
if (qspi_full_stop) |
begin |
state <= `WBQSPI_IDLE; |
o_wb_ack <= spif_req; |
o_wb_stall <= 1'b0; |
spif_req <= 1'b0; |
end |
|
// |
// |
// Write/erase data section |
// |
`ifndef READ_ONLY |
end else if (state == `WBQSPI_WAIT_WIP_CLEAR) |
begin |
o_wb_stall <= 1'b1; |
o_wb_ack <= 1'b0; |
spi_wr <= 1'b0; |
spif_req<= (spif_req) && (i_wb_cyc); |
if (~spi_busy) |
begin |
spi_wr <= 1'b1; |
// spi_in <= { 8'h05, 24'h0000 }; |
spi_hold <= 1'b1; |
spi_len <= 2'b01; // 16 bits write, so we can read 8 |
state <= `WBQSPI_CHECK_WIP_CLEAR; |
spi_spd <= 1'b0; // Slow speed |
spi_dir <= 1'b0; |
end |
end else if (state == `WBQSPI_CHECK_WIP_CLEAR) |
begin |
o_wb_stall <= 1'b1; |
o_wb_ack <= 1'b0; |
// Repeat as often as necessary until we are clear |
spi_wr <= 1'b1; |
// spi_in <= 32'h0000; // Values here are actually irrelevant |
spi_hold <= 1'b1; |
spi_len <= 2'b00; // One byte at a time |
spi_spd <= 1'b0; // Slow speed |
spi_dir <= 1'b0; |
spif_req<= (spif_req) && (i_wb_cyc); |
if ((spi_valid)&&(~spi_out[0])) |
begin |
state <= `WBQSPI_CHECK_WIP_DONE; |
spi_wr <= 1'b0; |
spi_hold <= 1'b0; |
write_in_progress <= 1'b0; |
last_status <= spi_out[7:0]; |
end |
end else if (state == `WBQSPI_CHECK_WIP_DONE) |
begin |
o_wb_stall <= 1'b1; |
o_wb_ack <= 1'b0; |
// Let's let the SPI port come back to a full idle, |
// and the chip select line go low before continuing |
spi_wr <= 1'b0; |
spi_len <= 2'b00; |
spi_hold <= 1'b0; |
spi_spd <= 1'b0; // Slow speed |
spi_dir <= 1'b0; |
spif_req<= (spif_req) && (i_wb_cyc); |
if ((o_qspi_cs_n)&&(~spi_busy)) // Chip select line is high, we can continue |
begin |
spi_wr <= 1'b0; |
spi_hold <= 1'b0; |
|
casez({ spif_cmd, spif_ctrl, spif_addr[1:0] }) |
4'b00??: begin // Read data from ... somewhere |
spi_wr <= 1'b1; // Write cmd to device |
if (quad_mode_enabled) |
begin |
// spi_in <= { 8'heb, |
// {(24-ADDRESS_WIDTH){1'b0}}, |
// spif_addr[(ADDRESS_WIDTH-3):0], 2'b00 }; |
state <= `WBQSPI_QRD_ADDRESS; |
// spi_len <= 2'b00; // single byte, cmd only |
end else begin |
// spi_in <= { 8'h0b, |
// {(24-ADDRESS_WIDTH){1'b0}}, |
// spif_addr[(ADDRESS_WIDTH-3):0], 2'b00 }; |
state <= `WBQSPI_RD_DUMMY; |
spi_len <= 2'b11; // Send cmd and addr |
end end |
4'b10??: begin // Write data to ... anywhere |
spi_wr <= 1'b1; |
spi_len <= 2'b00; // 8 bits |
// Send a write enable command |
// spi_in <= { 8'h06, 24'h00 }; |
state <= `WBQSPI_WEN; |
end |
4'b0110: begin // Read status register |
state <= `WBQSPI_READ_STATUS; |
spi_wr <= 1'b1; |
spi_len <= 2'b01; // 8 bits out, 8 bits in |
// spi_in <= { 8'h05, 24'h00}; |
end |
4'b0111: begin |
state <= `WBQSPI_READ_ID_CMD; |
spi_wr <= 1'b1; |
spi_len <= 2'b00; |
// spi_in <= { 8'h9f, 24'h00}; |
end |
default: begin // |
o_wb_stall <= 1'b1; |
o_wb_ack <= spif_req; |
state <= `WBQSPI_WAIT_TIL_IDLE; |
end |
endcase |
// spif_cmd <= i_wb_we; |
// spif_addr <= i_wb_addr; |
// spif_data <= i_wb_data; |
// spif_ctrl <= (i_wb_ctrl_stb)&&(~i_wb_data_stb); |
// spi_wr <= 1'b0; // Keep the port idle, unless told otherwise |
end |
end else if (state == `WBQSPI_WEN) |
begin // We came here after issuing a write enable command |
spi_wr <= 1'b0; |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
spif_req<= (spif_req) && (i_wb_cyc); |
if (qspi_full_stop) // Let's come to a full stop |
state <= (quad_mode_enabled)?`WBQSPI_QPP:`WBQSPI_PP; |
// state <= `WBQSPI_PP; |
end else if (state == `WBQSPI_PP) |
begin // We come here under a full stop / full port idle mode |
// Issue our command immediately |
spi_wr <= 1'b1; |
// spi_in <= { 8'h02, |
// {(24-ADDRESS_WIDTH){1'b0}}, |
// spif_addr[(ADDRESS_WIDTH-3):0], 2'b00 }; |
spi_len <= 2'b11; |
spi_hold <= 1'b1; |
spi_spd <= 1'b0; |
spi_dir <= 1'b0; // Writing |
spif_req<= (spif_req) && (i_wb_cyc); |
|
// Once we get busy, move on |
if (spi_busy) |
state <= `WBQSPI_WR_DATA; |
if (spif_sector == erased_sector) |
dirty_sector <= 1'b1; |
end else if (state == `WBQSPI_QPP) |
begin // We come here under a full stop / full port idle mode |
// Issue our command immediately |
spi_wr <= 1'b1; |
// spi_in <= { 8'h32, |
// {(24-ADDRESS_WIDTH){1'b0}}, |
// spif_addr[(ADDRESS_WIDTH-3):0], 2'b00 }; |
spi_len <= 2'b11; |
spi_hold <= 1'b1; |
spi_spd <= 1'b0; |
spi_dir <= 1'b0; // Writing |
spif_req<= (spif_req) && (i_wb_cyc); |
|
// Once we get busy, move on |
if (spi_busy) |
begin |
// spi_wr is irrelevant here ... |
// Set the speed value once, but wait til we get busy |
// to do so. |
spi_spd <= 1'b1; |
state <= `WBQSPI_WR_DATA; |
end |
if (spif_sector == erased_sector) |
dirty_sector <= 1'b1; |
end else if (state == `WBQSPI_WR_DATA) |
begin |
o_wb_stall <= 1'b1; |
o_wb_ack <= 1'b0; |
spi_wr <= 1'b1; // write without waiting |
// spi_in <= { |
// spif_data[ 7: 0], |
// spif_data[15: 8], |
// spif_data[23:16], |
// spif_data[31:24] }; |
spi_len <= 2'b11; // Write 4 bytes |
spi_hold <= 1'b1; |
if (~spi_busy) |
begin |
o_wb_ack <= spif_req; // Ack when command given |
state <= `WBQSPI_WR_BUS_CYCLE; |
end |
spif_req<= (spif_req) && (i_wb_cyc); |
end else if (state == `WBQSPI_WR_BUS_CYCLE) |
begin |
o_wb_ack <= 1'b0; // Turn off our ack and stall flags |
o_wb_stall <= 1'b1; |
spi_wr <= 1'b0; |
spi_hold <= 1'b1; |
write_in_progress <= 1'b1; |
spif_req<= (spif_req) && (i_wb_cyc); |
if (~i_wb_cyc) |
begin |
state <= `WBQSPI_WAIT_TIL_IDLE; |
spi_hold <= 1'b0; |
end else if (spi_wr) |
begin // Give the SPI a chance to get busy on the last write |
// Do nothing here. |
end else if (wr_same_row) |
begin |
spif_cmd <= 1'b1; |
spif_data <= i_wb_data; |
spif_addr <= i_wb_addr; |
spif_ctrl <= 1'b0; |
spif_req<= 1'b1; |
// We'll keep the bus stalled on this request |
// for a while |
state <= `WBQSPI_WR_DATA; |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b0; |
end else if ((i_wb_data_stb|i_wb_ctrl_stb)&&(~o_wb_ack)) // Writing out of bounds |
begin |
spi_hold <= 1'b0; |
spi_wr <= 1'b0; |
state <= `WBQSPI_WAIT_TIL_IDLE; |
end // Otherwise we stay here |
end else if (state == `WBQSPI_WRITE_CONFIG) |
begin // We enter immediately after commanding a WEN |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
|
spi_len <= 2'b10; |
// spi_in <= { 8'h01, last_status, 6'h0, spif_data[1:0], 8'h00 }; |
spi_wr <= 1'b0; |
spi_hold <= 1'b0; |
spif_req <= (spif_req) && (i_wb_cyc); |
if ((~spi_busy)&&(~spi_wr)) |
begin |
spi_wr <= 1'b1; |
state <= `WBQSPI_WAIT_TIL_IDLE; |
write_in_progress <= 1'b1; |
quad_mode_enabled <= spif_data[1]; |
end |
end else if (state == `WBQSPI_WRITE_STATUS) |
begin // We enter immediately after commanding a WEN |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
|
spi_len <= 2'b01; |
// spi_in <= { 8'h01, spif_data[7:0], 16'h00 }; |
// last_status <= i_wb_data[7:0]; // We'll read this in a moment |
spi_wr <= 1'b0; |
spi_hold <= 1'b0; |
spif_req <= (spif_req) && (i_wb_cyc); |
if ((~spi_busy)&&(~spi_wr)) |
begin |
spi_wr <= 1'b1; |
last_status <= spif_data[7:0]; |
write_in_progress <= 1'b1; |
if(((last_status[6])||(last_status[5])) |
&&((~spif_data[6])&&(~spif_data[5]))) |
state <= `WBQSPI_CLEAR_STATUS; |
else |
state <= `WBQSPI_WAIT_TIL_IDLE; |
end |
end else if (state == `WBQSPI_ERASE_CMD) |
begin // Know that WIP is clear on entry, WEN has just been commanded |
spi_wr <= 1'b0; |
o_wb_ack <= 1'b0; |
o_wb_stall <= 1'b1; |
spi_hold <= 1'b0; |
spi_spd <= 1'b0; |
spi_dir <= 1'b0; |
spif_req <= (spif_req) && (i_wb_cyc); |
|
// Here's the erase command |
// spi_in <= { 8'hd8, 2'h0, spif_data[19:14], 14'h000, 2'b00 }; |
spi_len <= 2'b11; // 32 bit write |
// together with setting our copy of the WIP bit |
write_in_progress <= 1'b1; |
// keeping track of which sector we just erased |
erased_sector <= spif_data[(ADDRESS_WIDTH-3):14]; |
// and marking this erase sector as no longer dirty |
dirty_sector <= 1'b0; |
|
// Wait for a full stop before issuing this command |
if ((~spi_busy)&&(~spi_wr)&&(o_qspi_cs_n)) |
begin // When our command is accepted, move to the next state |
spi_wr <= 1'b1; |
state <= `WBQSPI_ERASE_BLOCK; |
end |
end else if (state == `WBQSPI_ERASE_BLOCK) |
begin |
spi_wr <= 1'b0; |
spi_hold <= 1'b0; |
o_wb_stall <= 1'b1; |
o_wb_ack <= 1'b0; |
spif_req <= (spif_req) && (i_wb_cyc); |
// When the port clears, we can head back to idle |
if ((~spi_busy)&&(~spi_wr)) |
begin |
o_wb_ack <= spif_req; |
state <= `WBQSPI_IDLE; |
end |
end else if (state == `WBQSPI_CLEAR_STATUS) |
begin // Issue a clear status command |
spi_wr <= 1'b1; |
spi_hold <= 1'b0; |
spi_len <= 2'b00; // 8 bit command |
// spi_in <= { 8'h30, 24'h00 }; |
spi_spd <= 1'b0; |
spi_dir <= 1'b0; |
last_status[6:5] <= 2'b00; |
spif_req <= (spif_req) && (i_wb_cyc); |
if ((spi_wr)&&(~spi_busy)) |
state <= `WBQSPI_WAIT_TIL_IDLE; |
end else if (state == `WBQSPI_IDLE_CHECK_WIP) |
begin // We are now in read status register mode |
|
// No bus commands have (yet) been given |
o_wb_stall <= 1'b1; |
o_wb_ack <= 1'b0; |
spif_req <= (spif_req) && (i_wb_cyc); |
|
// Stay in this mode unless/until we get a command, or |
// the write is over |
spi_wr <= (((~i_wb_cyc)||((~i_wb_data_stb)&&(~i_wb_ctrl_stb))) |
&&(write_in_progress)); |
spi_len <= 2'b00; // 8 bit reads |
spi_spd <= 1'b0; // SPI, not quad |
spi_dir <= 1'b1; // Read |
if (spi_valid) |
begin |
write_in_progress <= spi_out[0]; |
if ((~spi_out[0])&&(write_in_progress)) |
o_interrupt <= 1'b1; |
end else |
o_interrupt <= 1'b0; |
|
if ((~spi_wr)&&(~spi_busy)&&(o_qspi_cs_n)) |
begin // We can now go to idle and process a command |
o_wb_stall <= 1'b0; |
o_wb_ack <= 1'b0; |
state <= `WBQSPI_IDLE; |
end |
`endif // !READ_ONLY |
end else // if (state == `WBQSPI_WAIT_TIL_IDLE) or anything else |
begin |
spi_wr <= 1'b0; |
spi_hold <= 1'b0; |
o_wb_stall <= 1'b1; |
o_wb_ack <= 1'b0; |
spif_req <= 1'b0; |
if ((~spi_busy)&&(o_qspi_cs_n)&&(~spi_wr)) // Wait for a full |
begin // clearing of the SPI port before moving on |
state <= `WBQSPI_IDLE; |
o_wb_stall <= 1'b0; |
o_wb_ack <= 1'b0; // Shouldn't be acking anything here |
end |
end |
end |
|
|
wire [23:0] bus_address, reg_address; |
generate |
if (ADDRESS_WIDTH<24) |
begin |
assign bus_address = { {(24-ADDRESS_WIDTH){1'b0}}, |
i_wb_addr[(ADDRESS_WIDTH-3):0], 2'b00 }; |
assign reg_address = { {(24-ADDRESS_WIDTH){1'b0}}, |
spif_addr[(ADDRESS_WIDTH-3):0], 2'b00 }; |
end else begin |
assign bus_address = { i_wb_addr[(ADDRESS_WIDTH-3):0], 2'b00 }; |
assign reg_address = { spif_addr[(ADDRESS_WIDTH-3):0], 2'b00 }; |
end endgenerate |
|
// Set the spi_in register, and that register only |
always @(posedge i_clk) |
begin |
if (state == `WBQSPI_IDLE) |
begin |
if (quad_mode_enabled) |
spi_in <= { 8'heb, bus_address }; |
else |
spi_in <= { 8'h0b, bus_address }; |
end else if (state == `WBQSPI_RDIDLE) |
begin |
spi_in <= 32'h00; |
if ((i_wb_data_stb)&&(~i_wb_we)) |
begin // Send the new address / mode |
spi_in <= { bus_address, 8'ha0 }; |
end |
end else if (state == `WBQSPI_WBDECODE) |
begin |
if ((~spi_busy)&&(o_qspi_cs_n)&&(~spi_wr)) // only in full idle ... |
begin |
// Data register access |
if (~spif_ctrl) |
begin |
spi_in <= { 8'h06, 24'h00 }; |
end else if ((spif_ctrl)&&(spif_cmd)) |
begin |
`ifdef READ_ONLY |
`else |
spi_in <= { 8'h06, 24'h00 }; |
`endif |
end else begin // on (~spif_we) |
case(spif_addr[1:0]) |
2'b00: ;// Read local register |
2'b01: // Read configuration register |
spi_in <= { 8'h35, 24'h00}; |
2'b10: // Read status register |
spi_in <= { 8'h05, 24'h00}; |
2'b11: // Read ID register |
spi_in <= { 8'h9f, 24'h00}; |
endcase |
end |
end |
end else if (state == `WBQSPI_RD_DUMMY) |
begin |
spi_in <= { 8'h00, 24'h00 }; |
end else if (state == `WBQSPI_QRD_ADDRESS) |
begin |
spi_in <= { reg_address, 8'ha0 }; |
end else if (state == `WBQSPI_QRD_DUMMY) |
begin |
spi_in <= { 8'ha0, 24'h00 }; // Mode byte, then 2 bytes dummy |
end else if (state == `WBQSPI_READ_CMD) |
begin // Issue our first command to read 32 bits. |
spi_in <= { 8'hff, 24'h00 }; // Empty |
end else if (state == `WBQSPI_READ_DATA) |
begin |
spi_in <= 32'h00; |
end else if (state == `WBQSPI_WAIT_TIL_RDIDLE) |
begin // Wait 'til idle, but then go to fast read idle instead of full |
end else if (state == `WBQSPI_READ_ID_CMD) |
begin // We came into here immediately after issuing a 0x9f command |
spi_in <= 32'h00; // Irrelevant |
end else if (state == `WBQSPI_READ_ID) |
begin |
end else if (state == `WBQSPI_READ_STATUS) |
begin // We enter after the command has been given, for now just |
end else if (state == `WBQSPI_READ_CONFIG) |
begin // We enter after the command has been given, for now just |
`ifndef READ_ONLY |
end else if (state == `WBQSPI_WAIT_WIP_CLEAR) |
begin |
if (~spi_busy) |
spi_in <= { 8'h05, 24'h0000 }; |
end else if (state == `WBQSPI_CHECK_WIP_CLEAR) |
begin |
spi_in <= 32'h0000; // Values here are actually irrelevant |
end else if (state == `WBQSPI_CHECK_WIP_DONE) |
begin |
if ((o_qspi_cs_n)&&(~spi_busy)) // Chip select line is high, we can continue |
begin |
// The bottom 24 bits are either don't cares, or the |
// address we want. Set them to be the address always, |
// to spare a touch on their logic. |
spi_in <= { 8'heb, reg_address }; |
casez({ spif_cmd, spif_ctrl, spif_addr[1:0] }) |
4'b00??: begin // Read data from ... somewhere |
if (quad_mode_enabled) |
begin |
spi_in[31:24] <= 8'heb; |
end else begin |
spi_in[31:24] <= 8'h0b; |
end end |
4'b10??: // Write data to ... anywhere |
spi_in[31:24] <= 8'h06 ; |
4'b0110: // Read status register |
spi_in[31:24] <= 8'h05 ; |
4'b0111: |
spi_in[31:24] <= 8'h9f ; |
default: begin // |
end |
endcase |
end |
end else if (state == `WBQSPI_WEN) |
begin // We came here after issuing a write enable command |
end else if (state == `WBQSPI_PP) |
begin // We come here under a full stop / full port idle mode |
spi_in <= { 8'h02, reg_address }; |
end else if (state == `WBQSPI_QPP) |
begin // We come here under a full stop / full port idle mode |
spi_in <= { 8'h32, reg_address }; |
end else if (state == `WBQSPI_WR_DATA) |
begin |
spi_in <= { |
spif_data[ 7: 0], |
spif_data[15: 8], |
spif_data[23:16], |
spif_data[31:24] }; |
end else if (state == `WBQSPI_WR_BUS_CYCLE) |
begin |
end else if (state == `WBQSPI_WRITE_CONFIG) |
begin // We enter immediately after commanding a WEN |
// spi_in <= { 8'h01, last_status, spif_data[7:0], 8'h00 }; |
spi_in <= { 8'h01, last_status, 6'h0, spif_data[1:0], 8'h00 }; |
end else if (state == `WBQSPI_WRITE_STATUS) |
begin // We enter immediately after commanding a WEN |
spi_in <= { 8'h01, spif_data[7:0], 16'h00 }; |
end else if (state == `WBQSPI_ERASE_CMD) |
begin // Know that WIP is clear on entry, WEN has just been commanded |
// Here's the erase command |
spi_in <= { 8'hd8, 2'h0, spif_data[19:14], 14'h000, 2'b00 }; |
end else if (state == `WBQSPI_ERASE_BLOCK) |
begin |
end else if (state == `WBQSPI_CLEAR_STATUS) |
begin // Issue a clear status command |
spi_in <= { 8'h30, 24'h00 }; |
end else if (state == `WBQSPI_IDLE_CHECK_WIP) |
begin // We are now in read status register mode |
`endif // !READ_ONLY |
end |
end |
|
|
initial wr_same_row = 1'b0; |
always @(posedge i_clk) |
wr_same_row <= ((i_wb_data_stb)&&(i_wb_we) |
&&(i_wb_addr[5:0] == (spif_addr[5:0]+6'h1)) |
&&(i_wb_addr[(ADDRESS_WIDTH-3):6] |
==spif_addr[(ADDRESS_WIDTH-3):6])); |
initial pipeline_read = 1'b0; |
always @(posedge i_clk) |
pipeline_read <=((i_wb_data_stb)&&(~i_wb_we)&&(i_wb_addr== (spif_addr+1))); |
|
// Command and control during the reset sequence |
assign o_qspi_cs_n = (spif_override)?spif_cmd :w_qspi_cs_n; |
assign o_qspi_sck = (spif_override)?spif_ctrl:w_qspi_sck; |
assign o_qspi_mod = (spif_override)? 2'b01 :w_qspi_mod; |
assign o_qspi_dat = (spif_override)? 4'b00 :w_qspi_dat; |
endmodule |
/rtl/fastio.v
0,0 → 1,458
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: fastio.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`include "builddate.v" |
// |
module fastio(i_clk, |
// Board level I/O |
i_sw, i_btn, o_led, |
o_clr_led0, o_clr_led1, o_clr_led2, o_clr_led3, |
// Board level PMod I/O |
i_aux_rx, o_aux_tx, o_aux_cts, i_gps_rx, o_gps_tx, |
// i_gpio, o_gpio, |
// Wishbone control |
i_wb_cyc, i_wb_stb, i_wb_we, i_wb_addr, |
i_wb_data, o_wb_ack, o_wb_stall, o_wb_data, |
// Cross-board I/O |
i_rtc_ppd, i_buserr, i_other_ints, o_bus_int, o_board_ints); |
parameter AUXUART_SETUP = 30'hd50, // 4M baud from 200MHz clock |
GPSUART_SETUP = 30'hd20833; // 9600 baud from 200MHz clk |
input i_clk; |
// Board level I/O |
input [3:0] i_sw; |
input [3:0] i_btn; |
output wire [3:0] o_led; |
output reg [2:0] o_clr_led0; |
output reg [2:0] o_clr_led1; |
output reg [2:0] o_clr_led2; |
output reg [2:0] o_clr_led3; |
// Board level PMod I/O |
// |
// Auxilliary UART I/O |
input i_aux_rx; |
output wire o_aux_tx, o_aux_cts; |
// |
// GPS UART I/O |
input i_gps_rx; |
output wire o_gps_tx; |
// |
// GPIO |
// input [(NGPI-1):0] i_gpio; |
// output reg [(NGPO-1):0] o_gpio; |
// |
// Wishbone inputs |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [4:0] i_wb_addr; |
input [31:0] i_wb_data; |
// Wishbone outputs |
output reg o_wb_ack; |
output wire o_wb_stall; |
output reg [31:0] o_wb_data; |
// A strobe at midnight, to keep the calendar on "time" |
input i_rtc_ppd; |
// Address of the last bus error |
input [31:0] i_buserr; |
// |
// Interrupts -- both the output bus interrupt, as well as those |
// internally generated interrupts which may be used elsewhere |
// in the design |
input wire [8:0] i_other_ints; |
output wire o_bus_int; |
output wire [5:0] o_board_ints; // Button and switch interrupts |
|
reg last_wb_stb; |
reg [4:0] last_wb_addr; |
reg [31:0] last_wb_data; |
initial last_wb_stb = 1'b0; |
always @(posedge i_clk) |
begin |
last_wb_addr <= i_wb_addr; |
last_wb_data <= i_wb_data; |
last_wb_stb <= (i_wb_stb)&&(i_wb_we); |
end |
|
wire [31:0] pic_data; |
reg sw_int, btn_int; |
wire pps_int, rtc_int, netrx_int, nettx_int, |
auxrx_int, auxtx_int, gpio_int, flash_int, scop_int, |
gpsrx_int, sd_int, oled_int, zip_int; |
assign { zip_int, oled_int, rtc_int, sd_int, |
nettx_int, netrx_int, scop_int, flash_int, |
pps_int } = i_other_ints; |
|
icontrol #(15) buspic(i_clk, 1'b0, |
(last_wb_stb)&&(last_wb_addr==5'h1), |
i_wb_data, pic_data, |
{ zip_int, oled_int, sd_int, |
gpsrx_int, scop_int, flash_int, gpio_int, |
auxtx_int, auxrx_int, nettx_int, netrx_int, |
rtc_int, pps_int, sw_int, btn_int }, |
o_bus_int); |
|
// |
// PWR Count |
// |
// A 32-bit counter that starts at power up and never resets. It's a |
// read only counter if you will. |
reg [31:0] pwr_counter; |
initial pwr_counter = 32'h00; |
always @(posedge i_clk) |
pwr_counter <= pwr_counter+32'h001; |
|
// |
// BTNSW |
// |
// The button and switch control register |
wire [31:0] w_btnsw; |
reg [3:0] r_sw, swcfg, swnow, swlast; |
reg [3:0] r_btn, btncfg, btnnow, btnlast, btnstate; |
initial btn_int = 1'b0; |
initial sw_int = 1'b0; |
always @(posedge i_clk) |
begin |
r_sw <= i_sw; |
swnow <= r_sw; |
swlast<= swnow; |
sw_int <= |((swnow^swlast)|swcfg); |
|
if ((last_wb_stb)&&(last_wb_addr == 5'h4)) |
swcfg <= ((last_wb_data[3:0])&(last_wb_data[11:8])) |
|((~last_wb_data[3:0])&(swcfg)); |
|
r_btn <= i_btn; |
btnnow <= r_btn; |
btn_int <= |(btnnow&btncfg); |
if ((last_wb_stb)&&(last_wb_addr == 5'h4)) |
begin |
btncfg <= ((last_wb_data[7:4])&(last_wb_data[15:12])) |
|((~last_wb_data[7:4])&(btncfg)); |
btnstate<= (btnnow)|((btnstate)&(~last_wb_data[7:4])); |
end else |
btnstate <= (btnstate)|(btnnow); |
end |
assign w_btnsw = { 8'h00, btnnow, 4'h0, btncfg, swcfg, btnstate, swnow }; |
|
// |
// LEDCTRL |
// |
reg [3:0] r_leds; |
wire [31:0] w_ledreg; |
reg last_cyc; |
always @(posedge i_clk) |
last_cyc <= i_wb_cyc; |
initial r_leds = 4'h0; |
always @(posedge i_clk) |
if ((last_wb_stb)&&(last_wb_addr == 5'h5)) |
r_leds <= last_wb_data[3:0]; |
assign o_led = r_leds; |
assign w_ledreg = { 28'h0, r_leds }; |
|
// |
// GPIO |
// |
// Not used (yet), but this interface should allow us to control up to |
// 16 GPIO inputs, and another 16 GPIO outputs. The interrupt trips |
// when any of the inputs changes. (Sorry, which input isn't (yet) |
// selectable.) |
// |
assign gpio_int = 1'b0; |
|
// |
// AUX (UART) SETUP |
// |
// Set us up for 4Mbaud, 8 data bits, no stop bits. |
reg [29:0] aux_setup; |
initial aux_setup = AUXUART_SETUP; |
always @(posedge i_clk) |
if ((last_wb_stb)&&(last_wb_addr == 5'h6)) |
aux_setup[29:0] <= last_wb_data[29:0]; |
|
// |
// GPSSETUP |
// |
// Set us up for 9600 kbaud, 8 data bits, no stop bits. |
reg [29:0] gps_setup; |
initial gps_setup = GPSUART_SETUP; |
always @(posedge i_clk) |
if ((last_wb_stb)&&(last_wb_addr == 5'h7)) |
gps_setup[29:0] <= last_wb_data[29:0]; |
|
// |
// CLR LEDs |
// |
|
// CLR LED 0 |
wire [31:0] w_clr_led0; |
reg [8:0] r_clr_led0_r, r_clr_led0_g, r_clr_led0_b; |
initial r_clr_led0_r = 9'h003; // Color LED on the far right |
initial r_clr_led0_g = 9'h000; |
initial r_clr_led0_b = 9'h000; |
always @(posedge i_clk) |
if ((last_wb_stb)&&(last_wb_addr == 5'h8)) |
begin |
r_clr_led0_r <= { last_wb_data[26], last_wb_data[23:16] }; |
r_clr_led0_g <= { last_wb_data[25], last_wb_data[15: 8] }; |
r_clr_led0_b <= { last_wb_data[24], last_wb_data[ 7: 0] }; |
end |
assign w_clr_led0 = { 5'h0, |
r_clr_led0_r[8], r_clr_led0_g[8], r_clr_led0_b[8], |
r_clr_led0_r[7:0], r_clr_led0_g[7:0], r_clr_led0_b[7:0] |
}; |
always @(posedge i_clk) |
o_clr_led0 <= { (pwr_counter[8:0] < r_clr_led0_r), |
(pwr_counter[8:0] < r_clr_led0_g), |
(pwr_counter[8:0] < r_clr_led0_b) }; |
|
// CLR LED 1 |
wire [31:0] w_clr_led1; |
reg [8:0] r_clr_led1_r, r_clr_led1_g, r_clr_led1_b; |
initial r_clr_led1_r = 9'h007; |
initial r_clr_led1_g = 9'h000; |
initial r_clr_led1_b = 9'h000; |
always @(posedge i_clk) |
if ((last_wb_stb)&&(last_wb_addr == 5'h9)) |
begin |
r_clr_led1_r <= { last_wb_data[26], last_wb_data[23:16] }; |
r_clr_led1_g <= { last_wb_data[25], last_wb_data[15: 8] }; |
r_clr_led1_b <= { last_wb_data[24], last_wb_data[ 7: 0] }; |
end |
assign w_clr_led1 = { 5'h0, |
r_clr_led1_r[8], r_clr_led1_g[8], r_clr_led1_b[8], |
r_clr_led1_r[7:0], r_clr_led1_g[7:0], r_clr_led1_b[7:0] |
}; |
always @(posedge i_clk) |
o_clr_led1 <= { (pwr_counter[8:0] < r_clr_led1_r), |
(pwr_counter[8:0] < r_clr_led1_g), |
(pwr_counter[8:0] < r_clr_led1_b) }; |
// CLR LED 0 |
wire [31:0] w_clr_led2; |
reg [8:0] r_clr_led2_r, r_clr_led2_g, r_clr_led2_b; |
initial r_clr_led2_r = 9'h00f; |
initial r_clr_led2_g = 9'h000; |
initial r_clr_led2_b = 9'h000; |
always @(posedge i_clk) |
if ((last_wb_stb)&&(last_wb_addr == 5'ha)) |
begin |
r_clr_led2_r <= { last_wb_data[26], last_wb_data[23:16] }; |
r_clr_led2_g <= { last_wb_data[25], last_wb_data[15: 8] }; |
r_clr_led2_b <= { last_wb_data[24], last_wb_data[ 7: 0] }; |
end |
assign w_clr_led2 = { 5'h0, |
r_clr_led2_r[8], r_clr_led2_g[8], r_clr_led2_b[8], |
r_clr_led2_r[7:0], r_clr_led2_g[7:0], r_clr_led2_b[7:0] |
}; |
always @(posedge i_clk) |
o_clr_led2 <= { (pwr_counter[8:0] < r_clr_led2_r), |
(pwr_counter[8:0] < r_clr_led2_g), |
(pwr_counter[8:0] < r_clr_led2_b) }; |
// CLR LED 3 |
wire [31:0] w_clr_led3; |
reg [8:0] r_clr_led3_r, r_clr_led3_g, r_clr_led3_b; |
initial r_clr_led3_r = 9'h01f; // LED is on far left |
initial r_clr_led3_g = 9'h000; |
initial r_clr_led3_b = 9'h000; |
always @(posedge i_clk) |
if ((last_wb_stb)&&(last_wb_addr == 5'hb)) |
begin |
r_clr_led3_r <= { last_wb_data[26], last_wb_data[23:16] }; |
r_clr_led3_g <= { last_wb_data[25], last_wb_data[15: 8] }; |
r_clr_led3_b <= { last_wb_data[24], last_wb_data[ 7: 0] }; |
end |
assign w_clr_led3 = { 5'h0, |
r_clr_led3_r[8], r_clr_led3_g[8], r_clr_led3_b[8], |
r_clr_led3_r[7:0], r_clr_led3_g[7:0], r_clr_led3_b[7:0] |
}; |
always @(posedge i_clk) |
o_clr_led3 <= { (pwr_counter[8:0] < r_clr_led3_r), |
(pwr_counter[8:0] < r_clr_led3_g), |
(pwr_counter[8:0] < r_clr_led3_b) }; |
|
// |
// The Calendar DATE |
// |
wire [31:0] date_data; |
`define GET_DATE |
`ifdef GET_DATE |
wire date_ack, date_stall; |
rtcdate thedate(i_clk, i_rtc_ppd, |
i_wb_cyc, last_wb_stb, (last_wb_addr==5'hc), last_wb_data, |
date_ack, date_stall, date_data); |
`else |
assign date_data = 32'h20160000; |
`endif |
|
////// |
// |
// The auxilliary UART |
// |
////// |
|
// First the receiver |
wire auxrx_stb, auxrx_break, auxrx_perr, auxrx_ferr, auxck_uart; |
wire [7:0] rx_data_aux_port; |
rxuart auxrx(i_clk, 1'b0, aux_setup, i_aux_rx, |
auxrx_stb, rx_data_aux_port, auxrx_break, |
auxrx_perr, auxrx_ferr, auxck_uart); |
|
wire [31:0] auxrx_data; |
reg [11:0] r_auxrx_data; |
always @(posedge i_clk) |
if (auxrx_stb) |
begin |
r_auxrx_data[11] <= auxrx_break; |
r_auxrx_data[10] <= auxrx_ferr; |
r_auxrx_data[ 9] <= auxrx_perr; |
r_auxrx_data[7:0]<= rx_data_aux_port; |
end |
always @(posedge i_clk) |
if(((i_wb_stb)&&(~i_wb_we)&&(i_wb_addr == 5'h0d))||(auxrx_stb)) |
r_auxrx_data[8] <= auxrx_stb; |
assign o_aux_cts = auxrx_stb; |
assign auxrx_data = { 20'h00, r_auxrx_data }; |
assign auxrx_int = r_auxrx_data[8]; |
|
|
// Then the transmitter |
wire auxtx_busy; |
reg [7:0] r_auxtx_data; |
reg r_auxtx_stb, r_auxtx_break; |
wire [31:0] auxtx_data; |
txuart auxtx(i_clk, 1'b0, aux_setup, |
r_auxtx_break, r_auxtx_stb, r_auxtx_data, |
o_aux_tx, auxtx_busy); |
always @(posedge i_clk) |
if ((last_wb_stb)&&(last_wb_addr == 5'h0e)) |
begin |
r_auxtx_stb <= 1'b1; |
r_auxtx_data <= last_wb_data[7:0]; |
r_auxtx_break<= last_wb_data[9]; |
end else if (~auxtx_busy) |
begin |
r_auxtx_stb <= 1'b0; |
r_auxtx_data <= 8'h0; |
end |
assign auxtx_data = { 20'h00, |
auxck_uart, o_aux_tx, r_auxtx_break, auxtx_busy, |
r_auxtx_data }; |
assign auxtx_int = ~auxtx_busy; |
|
////// |
// |
// The GPS UART |
// |
////// |
|
// First the receiver |
wire gpsrx_stb, gpsrx_break, gpsrx_perr, gpsrx_ferr, gpsck_uart; |
wire [7:0] rx_data_gps_port; |
rxuart gpsrx(i_clk, 1'b0, gps_setup, i_gps_rx, |
gpsrx_stb, rx_data_gps_port, gpsrx_break, |
gpsrx_perr, gpsrx_ferr, gpsck_uart); |
|
wire [31:0] gpsrx_data; |
reg [11:0] r_gpsrx_data; |
always @(posedge i_clk) |
if (gpsrx_stb) |
begin |
r_gpsrx_data[11] <= gpsrx_break; |
r_gpsrx_data[10] <= gpsrx_ferr; |
r_gpsrx_data[ 9] <= gpsrx_perr; |
r_gpsrx_data[7:0]<= rx_data_gps_port; |
end |
always @(posedge i_clk) |
if(((i_wb_stb)&&(~i_wb_we)&&(i_wb_addr == 5'h0d))||(gpsrx_stb)) |
r_gpsrx_data[8] <= gpsrx_stb; |
assign gpsrx_data = { 20'h00, r_gpsrx_data }; |
assign gpsrx_int = r_gpsrx_data[8]; |
|
|
// Then the transmitter |
reg r_gpstx_break, r_gpstx_stb; |
reg [7:0] r_gpstx_data; |
wire gpstx_busy; |
wire [31:0] gpstx_data; |
txuart gpstx(i_clk, 1'b0, gps_setup, |
r_gpstx_break, r_gpstx_stb, r_gpstx_data, |
o_gps_tx, gpstx_busy); |
always @(posedge i_clk) |
if ((last_wb_stb)&&(last_wb_addr == 5'h0e)) |
begin |
r_gpstx_stb <= 1'b1; |
r_gpstx_data <= last_wb_data[7:0]; |
r_gpstx_break<= last_wb_data[9]; |
end else if (~gpstx_busy) |
begin |
r_gpstx_stb <= 1'b0; |
r_gpstx_data <= 8'h0; |
end |
assign gpstx_data = { 20'h00, |
gpsck_uart, o_gps_tx, r_gpstx_break, gpstx_busy, |
r_gpstx_data }; |
|
always @(posedge i_clk) |
case(i_wb_addr) |
5'h00: o_wb_data <= `DATESTAMP; |
5'h01: o_wb_data <= pic_data; |
5'h02: o_wb_data <= i_buserr; |
5'h03: o_wb_data <= pwr_counter; |
5'h04: o_wb_data <= w_btnsw; |
5'h05: o_wb_data <= w_ledreg; |
5'h06: o_wb_data <= { 2'b00, aux_setup }; |
5'h07: o_wb_data <= { 2'b00, gps_setup }; |
5'h08: o_wb_data <= w_clr_led0; |
5'h09: o_wb_data <= w_clr_led1; |
5'h0a: o_wb_data <= w_clr_led2; |
5'h0b: o_wb_data <= w_clr_led3; |
5'h0c: o_wb_data <= date_data; |
5'h0d: o_wb_data <= auxrx_data; |
5'h0e: o_wb_data <= auxtx_data; |
5'h10: o_wb_data <= gpsrx_data; |
5'h11: o_wb_data <= gpstx_data; |
// 5'hf: UART_SETUP |
// 4'h6: GPIO |
// ?? : GPS-UARTRX |
// ?? : GPS-UARTTX |
default: o_wb_data <= 32'h00; |
endcase |
|
assign o_wb_stall = 1'b0; |
always @(posedge i_clk) |
o_wb_ack <= (i_wb_stb); |
assign o_board_ints = { gpio_int, auxrx_int, auxtx_int, gpsrx_int, sw_int, btn_int }; |
|
|
endmodule |
/rtl/rxuart.v
0,0 → 1,330
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: rxuart.v |
// |
// Project: FPGA library |
// |
// Purpose: Receive and decode inputs from a single UART line. |
// |
// |
// To interface with this module, connect it to your system clock, |
// pass it the 32 bit setup register (defined below) and the UART |
// input. When data becomes available, the o_wr line will be asserted |
// for one clock cycle. On parity or frame errors, the o_parity_err |
// or o_frame_err lines will be asserted. Likewise, on a break |
// condition, o_break will be asserted. These lines are self clearing. |
// |
// There is a synchronous reset line, logic high. |
// |
// Now for the setup register. The register is 32 bits, so that this |
// UART may be set up over a 32-bit bus. |
// |
// i_setup[29:28] Indicates the number of data bits per word. This will |
// either be 2'b00 for an 8-bit word, 2'b01 for a 7-bit word, 2'b10 |
// for a six bit word, or 2'b11 for a five bit word. |
// |
// i_setup[27] Indicates whether or not to use one or two stop bits. |
// Set this to one to expect two stop bits, zero for one. |
// |
// i_setup[26] Indicates whether or not a parity bit exists. Set this |
// to 1'b1 to include parity. |
// |
// i_setup[25] Indicates whether or not the parity bit is fixed. Set |
// to 1'b1 to include a fixed bit of parity, 1'b0 to allow the |
// parity to be set based upon data. (Both assume the parity |
// enable value is set.) |
// |
// i_setup[24] This bit is ignored if parity is not used. Otherwise, |
// in the case of a fixed parity bit, this bit indicates whether |
// mark (1'b1) or space (1'b0) parity is used. Likewise if the |
// parity is not fixed, a 1'b1 selects even parity, and 1'b0 |
// selects odd. |
// |
// i_setup[23:0] Indicates the speed of the UART in terms of clocks. |
// So, for example, if you have a 200 MHz clock and wish to |
// run your UART at 9600 baud, you would take 200 MHz and divide |
// by 9600 to set this value to 24'd20834. Likewise if you wished |
// to run this serial port at 115200 baud from a 200 MHz clock, |
// you would set the value to 24'd1736 |
// |
// Thus, to set the UART for the common setting of an 8-bit word, |
// one stop bit, no parity, and 115200 baud over a 200 MHz clock, you |
// would want to set the setup value to: |
// |
// 32'h0006c8 // For 115,200 baud, 8 bit, no parity |
// 32'h005161 // For 9600 baud, 8 bit, no parity |
// |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
// States: (@ baud counter == 0) |
// 0 First bit arrives |
// ..7 Bits arrive |
// 8 Stop bit (x1) |
// 9 Stop bit (x2) |
/// c break condition |
// d Waiting for the channel to go high |
// e Waiting for the reset to complete |
// f Idle state |
`define RXU_BIT_ZERO 4'h0 |
`define RXU_BIT_ONE 4'h1 |
`define RXU_BIT_TWO 4'h2 |
`define RXU_BIT_THREE 4'h3 |
`define RXU_BIT_FOUR 4'h4 |
`define RXU_BIT_FIVE 4'h5 |
`define RXU_BIT_SIX 4'h6 |
`define RXU_BIT_SEVEN 4'h7 |
`define RXU_PARITY 4'h8 |
`define RXU_STOP 4'h9 |
`define RXU_SECOND_STOP 4'ha |
// Unused 4'hb |
// Unused 4'hc |
`define RXU_BREAK 4'hd |
`define RXU_RESET_IDLE 4'he |
`define RXU_IDLE 4'hf |
|
module rxuart(i_clk, i_reset, i_setup, i_uart, o_wr, o_data, o_break, |
o_parity_err, o_frame_err, o_ck_uart); |
// parameter // CLOCKS_PER_BAUD = 25'd004340, |
// BREAK_CONDITION = CLOCKS_PER_BAUD * 12, |
// CLOCKS_PER_HALF_BAUD = CLOCKS_PER_BAUD/2; |
// 8 data bits, no parity, (at least 1) stop bit |
input i_clk, i_reset; |
input [29:0] i_setup; |
input i_uart; |
output reg o_wr; |
output reg [7:0] o_data; |
output reg o_break; |
output reg o_parity_err, o_frame_err; |
output wire o_ck_uart; |
|
|
wire [27:0] clocks_per_baud, break_condition, half_baud; |
wire [1:0] data_bits; |
wire use_parity, parity_even, dblstop, fixd_parity; |
reg [29:0] r_setup; |
assign clocks_per_baud = { 4'h0, r_setup[23:0] }; |
assign data_bits = r_setup[29:28]; |
assign dblstop = r_setup[27]; |
assign use_parity = r_setup[26]; |
assign fixd_parity = r_setup[25]; |
assign parity_even = r_setup[24]; |
assign break_condition = { r_setup[23:0], 4'h0 }; |
assign half_baud = { 5'h00, r_setup[23:1] }; |
|
reg q_uart, qq_uart, ck_uart; |
initial q_uart = 1'b0; |
initial qq_uart = 1'b0; |
initial ck_uart = 1'b0; |
always @(posedge i_clk) |
begin |
q_uart <= i_uart; |
qq_uart <= q_uart; |
ck_uart <= qq_uart; |
end |
assign o_ck_uart = ck_uart; |
|
reg [27:0] chg_counter; |
initial chg_counter = 28'h00; |
always @(posedge i_clk) |
if (i_reset) |
chg_counter <= 28'h00; |
else if (qq_uart != ck_uart) |
chg_counter <= 28'h00; |
else if (chg_counter < break_condition) |
chg_counter <= chg_counter + 1; |
|
reg line_synch; |
initial line_synch = 1'b0; |
initial o_break = 1'b0; |
always @(posedge i_clk) |
o_break <= ((chg_counter >= break_condition)&&(~ck_uart))? 1'b1:1'b0; |
always @(posedge i_clk) |
line_synch <= ((chg_counter >= break_condition)&&(ck_uart)); |
|
reg [3:0] state; |
reg [27:0] baud_counter; |
reg [7:0] data_reg; |
reg calc_parity, zero_baud_counter, half_baud_time; |
initial o_wr = 1'b0; |
initial state = `RXU_RESET_IDLE; |
initial o_parity_err = 1'b0; |
initial o_frame_err = 1'b0; |
// initial baud_counter = clocks_per_baud; |
always @(posedge i_clk) |
begin |
if (i_reset) |
begin |
o_wr <= 1'b0; |
o_data <= 8'h00; |
state <= `RXU_RESET_IDLE; |
baud_counter <= clocks_per_baud-28'h01;// Set, not reset |
data_reg <= 8'h00; |
calc_parity <= 1'b0; |
o_parity_err <= 1'b0; |
o_frame_err <= 1'b0; |
end else if (state == `RXU_RESET_IDLE) |
begin |
r_setup <= i_setup; |
data_reg <= 8'h00; o_data <= 8'h00; o_wr <= 1'b0; |
baud_counter <= clocks_per_baud-28'h01;// Set, not reset |
if (line_synch) |
// Goto idle state from a reset |
state <= `RXU_IDLE; |
else // Otherwise, stay in this condition 'til reset |
state <= `RXU_RESET_IDLE; |
calc_parity <= 1'b0; |
o_parity_err <= 1'b0; |
o_frame_err <= 1'b0; |
end else if (o_break) |
begin // We are in a break condition |
state <= `RXU_BREAK; |
o_wr <= 1'b0; |
o_data <= 8'h00; |
baud_counter <= clocks_per_baud-28'h01;// Set, not reset |
data_reg <= 8'h00; |
calc_parity <= 1'b0; |
o_parity_err <= 1'b0; |
o_frame_err <= 1'b0; |
r_setup <= i_setup; |
end else if (state == `RXU_BREAK) |
begin // Goto idle state following return ck_uart going high |
data_reg <= 8'h00; o_data <= 8'h00; o_wr <= 1'b0; |
baud_counter <= clocks_per_baud - 28'h01; |
if (ck_uart) |
state <= `RXU_IDLE; |
else |
state <= `RXU_BREAK; |
calc_parity <= 1'b0; |
o_parity_err <= 1'b0; |
o_frame_err <= 1'b0; |
r_setup <= i_setup; |
end else if (state == `RXU_IDLE) |
begin // Idle state, independent of baud counter |
r_setup <= i_setup; |
data_reg <= 8'h00; o_data <= 8'h00; o_wr <= 1'b0; |
baud_counter <= clocks_per_baud - 28'h01; |
if ((~ck_uart)&&(half_baud_time)) |
begin |
// We are in the center of a valid start bit |
case (data_bits) |
2'b00: state <= `RXU_BIT_ZERO; |
2'b01: state <= `RXU_BIT_ONE; |
2'b10: state <= `RXU_BIT_TWO; |
2'b11: state <= `RXU_BIT_THREE; |
endcase |
end else // Otherwise, just stay here in idle |
state <= `RXU_IDLE; |
calc_parity <= 1'b0; |
o_parity_err <= 1'b0; |
o_frame_err <= 1'b0; |
end else if (zero_baud_counter) |
begin |
baud_counter <= clocks_per_baud-28'h1; |
if (state < `RXU_BIT_SEVEN) |
begin |
// Data arrives least significant bit first. |
// By the time this is clocked in, it's what |
// you'll have. |
data_reg <= { ck_uart, data_reg[7:1] }; |
calc_parity <= calc_parity ^ ck_uart; |
o_data <= 8'h00; |
o_wr <= 1'b0; |
state <= state + 1; |
o_parity_err <= 1'b0; |
o_frame_err <= 1'b0; |
end else if (state == `RXU_BIT_SEVEN) |
begin |
data_reg <= { ck_uart, data_reg[7:1] }; |
calc_parity <= calc_parity ^ ck_uart; |
o_data <= 8'h00; |
o_wr <= 1'b0; |
state <= (use_parity) ? `RXU_PARITY:`RXU_STOP; |
o_parity_err <= 1'b0; |
o_frame_err <= 1'b0; |
end else if (state == `RXU_PARITY) |
begin |
if (fixd_parity) |
o_parity_err <= (ck_uart ^ parity_even); |
else |
o_parity_err <= ((parity_even && (calc_parity != ck_uart)) |
||((~parity_even)&&(calc_parity==ck_uart))); |
state <= `RXU_STOP; |
o_frame_err <= 1'b0; |
end else if (state == `RXU_STOP) |
begin // Stop (or parity) bit(s) |
case (data_bits) |
2'b00: o_data <= data_reg; |
2'b01: o_data <= { 1'b0, data_reg[7:1] }; |
2'b10: o_data <= { 2'b0, data_reg[7:2] }; |
2'b11: o_data <= { 3'b0, data_reg[7:3] }; |
endcase |
o_wr <= 1'b1; // Pulse the write |
o_frame_err <= (~ck_uart); |
if (~ck_uart) |
state <= `RXU_RESET_IDLE; |
else if (dblstop) |
state <= `RXU_SECOND_STOP; |
else |
state <= `RXU_IDLE; |
// o_parity_err <= 1'b0; |
end else // state must equal RX_SECOND_STOP |
begin |
if (~ck_uart) |
begin |
o_frame_err <= 1'b1; |
state <= `RXU_RESET_IDLE; |
end else begin |
state <= `RXU_IDLE; |
o_frame_err <= 1'b0; |
end |
o_parity_err <= 1'b0; |
end |
end else begin |
o_wr <= 1'b0; // data_reg = data_reg |
baud_counter <= baud_counter - 28'd1; |
o_parity_err <= 1'b0; |
o_frame_err <= 1'b0; |
end |
end |
|
initial zero_baud_counter = 1'b0; |
always @(posedge i_clk) |
zero_baud_counter <= (baud_counter == 28'h01); |
|
initial half_baud_time = 0; |
always @(posedge i_clk) |
half_baud_time <= (~ck_uart)&&(chg_counter >= half_baud); |
|
|
endmodule |
|
|
/rtl/txuart.v
0,0 → 1,252
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: txuart.v |
// |
// Project: FPGA library |
// |
// Purpose: Transmit outputs over a single UART line. |
// |
// To interface with this module, connect it to your system clock, |
// pass it the 32 bit setup register (defined below) and the byte |
// of data you wish to transmit. Strobe the i_wr line high for one |
// clock cycle, and your data will be off. Wait until the 'o_busy' |
// line is low before strobing the i_wr line again--this implementation |
// has NO BUFFER, so strobing i_wr while the core is busy will just |
// cause your data to be lost. The output will be placed on the o_txuart |
// output line. If you wish to set/send a break condition, assert the |
// i_break line otherwise leave it low. |
// |
// There is a synchronous reset line, logic high. |
// |
// Now for the setup register. The register is 32 bits, so that this |
// UART may be set up over a 32-bit bus. |
// |
// i_setup[29:28] Indicates the number of data bits per word. This will |
// either be 2'b00 for an 8-bit word, 2'b01 for a 7-bit word, 2'b10 |
// for a six bit word, or 2'b11 for a five bit word. |
// |
// i_setup[27] Indicates whether or not to use one or two stop bits. |
// Set this to one to expect two stop bits, zero for one. |
// |
// i_setup[26] Indicates whether or not a parity bit exists. Set this |
// to 1'b1 to include parity. |
// |
// i_setup[25] Indicates whether or not the parity bit is fixed. Set |
// to 1'b1 to include a fixed bit of parity, 1'b0 to allow the |
// parity to be set based upon data. (Both assume the parity |
// enable value is set.) |
// |
// i_setup[24] This bit is ignored if parity is not used. Otherwise, |
// in the case of a fixed parity bit, this bit indicates whether |
// mark (1'b1) or space (1'b0) parity is used. Likewise if the |
// parity is not fixed, a 1'b1 selects even parity, and 1'b0 |
// selects odd. |
// |
// i_setup[23:0] Indicates the speed of the UART in terms of clocks. |
// So, for example, if you have a 200 MHz clock and wish to |
// run your UART at 9600 baud, you would take 200 MHz and divide |
// by 9600 to set this value to 24'd20834. Likewise if you wished |
// to run this serial port at 115200 baud from a 200 MHz clock, |
// you would set the value to 24'd1736 |
// |
// Thus, to set the UART for the common setting of an 8-bit word, |
// one stop bit, no parity, and 115200 baud over a 200 MHz clock, you |
// would want to set the setup value to: |
// |
// 32'h0006c8 // For 115,200 baud, 8 bit, no parity |
// 32'h005161 // For 9600 baud, 8 bit, no parity |
// |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
// |
`define TXU_BIT_ZERO 4'h0 |
`define TXU_BIT_ONE 4'h1 |
`define TXU_BIT_TWO 4'h2 |
`define TXU_BIT_THREE 4'h3 |
`define TXU_BIT_FOUR 4'h4 |
`define TXU_BIT_FIVE 4'h5 |
`define TXU_BIT_SIX 4'h6 |
`define TXU_BIT_SEVEN 4'h7 |
`define TXU_PARITY 4'h8 // Constant 1 |
`define TXU_STOP 4'h9 // Constant 1 |
`define TXU_SECOND_STOP 4'ha |
// 4'hb // Unused |
// 4'hc // Unused |
// `define TXU_START 4'hd // An unused state |
`define TXU_BREAK 4'he |
`define TXU_IDLE 4'hf |
// |
// |
module txuart(i_clk, i_reset, i_setup, i_break, i_wr, i_data, o_uart, o_busy); |
input i_clk, i_reset; |
input [29:0] i_setup; |
input i_break; |
input i_wr; |
input [7:0] i_data; |
output reg o_uart; |
output wire o_busy; |
|
wire [27:0] clocks_per_baud, break_condition; |
wire [1:0] data_bits; |
wire use_parity, parity_even, dblstop, fixd_parity; |
reg [29:0] r_setup; |
assign clocks_per_baud = { 4'h0, r_setup[23:0] }; |
assign break_condition = { r_setup[23:0], 4'h0 }; |
assign data_bits = r_setup[29:28]; |
assign dblstop = r_setup[27]; |
assign use_parity = r_setup[26]; |
assign fixd_parity = r_setup[25]; |
assign parity_even = r_setup[24]; |
|
reg [27:0] baud_counter; |
reg [3:0] state; |
reg [7:0] lcl_data; |
reg calc_parity, r_busy, zero_baud_counter; |
|
initial o_uart = 1'b1; |
initial r_busy = 1'b1; |
initial state = `TXU_IDLE; |
initial lcl_data= 8'h0; |
initial calc_parity = 1'b0; |
// initial baud_counter = clocks_per_baud;//ILLEGAL--not constant |
always @(posedge i_clk) |
begin |
if (i_reset) |
begin |
o_uart <= 1'b1; |
r_busy <= 1'b1; |
state <= `TXU_IDLE; |
lcl_data <= 8'h0; |
calc_parity <= 1'b0; |
end else if (i_break) |
begin |
o_uart <= 1'b0; |
state <= `TXU_BREAK; |
calc_parity <= 1'b0; |
r_busy <= 1'b1; |
end else if (~zero_baud_counter) |
begin // r_busy needs to be set coming into here |
r_busy <= 1'b1; |
end else if (state == `TXU_BREAK) |
begin |
state <= `TXU_IDLE; |
r_busy <= 1'b1; |
o_uart <= 1'b1; |
calc_parity <= 1'b0; |
end else if (state == `TXU_IDLE) // STATE_IDLE |
begin |
// baud_counter <= 0; |
r_setup <= i_setup; |
calc_parity <= 1'b0; |
if ((i_wr)&&(~r_busy)) |
begin // Immediately start us off with a start bit |
o_uart <= 1'b0; |
r_busy <= 1'b1; |
case(data_bits) |
2'b00: state <= `TXU_BIT_ZERO; |
2'b01: state <= `TXU_BIT_ONE; |
2'b10: state <= `TXU_BIT_TWO; |
2'b11: state <= `TXU_BIT_THREE; |
endcase |
lcl_data <= i_data; |
// baud_counter <= clocks_per_baud-28'h01; |
end else begin // Stay in idle |
o_uart <= 1'b1; |
r_busy <= 0; |
// lcl_data is irrelevant |
// state <= state; |
end |
end else begin |
// One clock tick in each of these states ... |
// baud_counter <= clocks_per_baud - 28'h01; |
r_busy <= 1'b1; |
if (state[3] == 0) // First 8 bits |
begin |
o_uart <= lcl_data[0]; |
calc_parity <= calc_parity ^ lcl_data[0]; |
if (state == `TXU_BIT_SEVEN) |
state <= (use_parity)?`TXU_PARITY:`TXU_STOP; |
else |
state <= state + 1; |
lcl_data <= { 1'b0, lcl_data[7:1] }; |
end else if (state == `TXU_PARITY) |
begin |
state <= `TXU_STOP; |
if (fixd_parity) |
o_uart <= parity_even; |
else |
o_uart <= calc_parity^((parity_even)? 1'b1:1'b0); |
end else if (state == `TXU_STOP) |
begin // two stop bit(s) |
o_uart <= 1'b1; |
if (dblstop) |
state <= `TXU_SECOND_STOP; |
else |
state <= `TXU_IDLE; |
calc_parity <= 1'b0; |
end else // `TXU_SECOND_STOP and default: |
begin |
state <= `TXU_IDLE; // Go back to idle |
o_uart <= 1'b1; |
// Still r_busy, since we need to wait |
// for the baud clock to finish counting |
// out this last bit. |
end |
end |
end |
|
assign o_busy = (r_busy); |
|
|
initial zero_baud_counter = 1'b0; |
always @(posedge i_clk) |
begin |
zero_baud_counter <= (baud_counter == 28'h01); |
if ((i_reset)||(i_break)) |
// Give ourselves 16 bauds before being ready |
baud_counter <= break_condition; |
else if (~zero_baud_counter) |
baud_counter <= baud_counter - 28'h01; |
else if (state == `TXU_BREAK) |
// Give us two stop bits before becoming available |
baud_counter <= clocks_per_baud<<2; |
else if (state == `TXU_IDLE) |
begin |
if((i_wr)&&(~r_busy)) |
baud_counter <= clocks_per_baud - 28'h01; |
else |
zero_baud_counter <= 1'b1; |
end else |
baud_counter <= clocks_per_baud - 28'h01; |
end |
endmodule |
|
/rtl/icontrol.v
0,0 → 1,153
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: icontrol.v |
// |
// Project: Zip CPU -- a small, lightweight, RISC CPU soft core |
// |
// Purpose: An interrupt controller, for managing many interrupt sources. |
// |
// This interrupt controller started from the question of how best to |
// design a simple interrupt controller. As such, it has a few nice |
// qualities to it: |
// 1. This is wishbone compliant |
// 2. It sits on a 32-bit wishbone data bus |
// 3. It only consumes one address on that wishbone bus. |
// 4. There is no extra delays associated with reading this |
// device. |
// 5. Common operations can all be done in one clock. |
// |
// So, how shall this be used? First, the 32-bit word is broken down as |
// follows: |
// |
// Bit 31 - This is the global interrupt enable bit. If set, interrupts |
// will be generated and passed on as they come in. |
// Bits 16-30 - These are specific interrupt enable lines. If set, |
// interrupts from source (bit#-16) will be enabled. |
// To set this line and enable interrupts from this source, write |
// to the register with this bit set and the global enable set. |
// To disable this line, write to this register with global enable |
// bit not set, but this bit set. (Writing a zero to any of these |
// bits has no effect, either setting or unsetting them.) |
// Bit 15 - This is the any interrupt pin. If any interrupt is pending, |
// this bit will be set. |
// Bits 0-14 - These are interrupt bits. When set, an interrupt is |
// pending from the corresponding source--regardless of whether |
// it was enabled. (If not enabled, it won't generate an |
// interrupt, but it will still register here.) To clear any |
// of these bits, write a '1' to the corresponding bit. Writing |
// a zero to any of these bits has no effect. |
// |
// The peripheral also sports a parameter, IUSED, which can be set |
// to any value between 1 and (buswidth/2-1, or) 15 inclusive. This will |
// be the number of interrupts handled by this routine. (Without the |
// parameter, Vivado was complaining about unused bits. With it, we can |
// keep the complaints down and still use the routine). |
// |
// To get access to more than 15 interrupts, chain these together, so |
// that one interrupt controller device feeds another. |
// |
// |
// 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. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
module icontrol(i_clk, i_reset, i_wr, i_proc_bus, o_proc_bus, |
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; |
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; |
|
assign nxt_int_state = (r_int_state|i_brd_ints); |
initial r_int_state = 0; |
always @(posedge i_clk) |
if (i_reset) |
r_int_state <= 0; |
else if (i_wr) |
r_int_state <= nxt_int_state & (~i_proc_bus[(IUSED-1):0]); |
else |
r_int_state <= nxt_int_state; |
initial r_int_enable = 0; |
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]); |
|
initial r_gie = 1'b0; |
always @(posedge i_clk) |
if (i_reset) |
r_gie <= 1'b0; |
else if (i_wr) |
r_gie <= i_proc_bus[31]; |
|
initial r_any = 1'b0; |
always @(posedge i_clk) |
r_any <= ((r_int_state & r_int_enable) != 0); |
initial r_interrupt = 1'b0; |
always @(posedge i_clk) |
r_interrupt <= r_gie & r_any; |
|
generate |
if (IUSED < 15) |
begin |
assign o_proc_bus = { |
r_gie, { {(15-IUSED){1'b0}}, r_int_enable }, |
r_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 }; |
end endgenerate |
|
/* |
reg int_condition; |
initial int_condition = 1'b0; |
initial o_interrupt_strobe = 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; |
*/ |
|
assign o_interrupt = r_interrupt; |
|
endmodule |
/rtl/builddate.v
0,0 → 1,41
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: builddate.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: This file records the date of the last build. Running "make" |
// in the main directory will create this file. The `define found |
// within it then creates a version stamp that can be used to tell which |
// configuration is within an FPGA and so forth. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`define DATESTAMP 32'h20160725 |
/rtl/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 |
/rtl/wbscope.v
0,0 → 1,341
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbscope.v |
// |
// Project: FPGA Library of Routines |
// |
// Purpose: This is a generic/library routine for providing a bus accessed |
// 'scope' or (perhaps more appropriately) a bus accessed logic |
// analyzer. The general operation is such that this 'scope' can |
// record and report on any 32 bit value transiting through the |
// FPGA. Once started and reset, the scope records a copy of the |
// input data every time the clock ticks with the circuit enabled. |
// That is, it records these values up until the trigger. Once |
// the trigger goes high, the scope will record for bw_holdoff |
// more counts before stopping. Values may then be read from the |
// buffer, oldest to most recent. After reading, the scope may |
// then be reset for another run. |
// |
// In general, therefore, operation happens in this fashion: |
// 1. A reset is issued. |
// 2. Recording starts, in a circular buffer, and continues until |
// 3. The trigger line is asserted. |
// The scope registers the asserted trigger by setting |
// the 'o_triggered' output flag. |
// 4. A counter then ticks until the last value is written |
// The scope registers that it has stopped recording by |
// setting the 'o_stopped' output flag. |
// 5. The scope recording is then paused until the next reset. |
// 6. While stopped, the CPU can read the data from the scope |
// 7. -- oldest to most recent |
// 8. -- one value per i_rd&i_clk |
// 9. Writes to the data register reset the address to the |
// beginning of the buffer |
// |
// Although the data width DW is parameterized, it is not very changable, |
// since the width is tied to the width of the data bus, as is the |
// control word. Therefore changing the data width would require changing |
// the interface. It's doable, but it would be a change to the interface. |
// |
// The SYNCHRONOUS parameter turns on and off meta-stability |
// synchronization. Ideally a wishbone scope able to handle one or two |
// clocks would have a changing number of ports as this SYNCHRONOUS |
// parameter changed. Other than running another script to modify |
// this, I don't know how to do that so ... we'll just leave it running |
// off of two clocks or not. |
// |
// |
// Internal to this routine, registers and wires are named with one of the |
// following prefixes: |
// |
// i_ An input port to the routine |
// o_ An output port of the routine |
// br_ A register, controlled by the bus clock |
// dr_ A register, controlled by the data clock |
// bw_ A wire/net, controlled by the bus clock |
// dw_ A wire/net, controlled by the data clock |
// |
// 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 wbscope(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, BUSW = 32, SYNCHRONOUS=1; |
// The input signals that we wish to record |
input i_clk, i_ce, i_trigger; |
input [(BUSW-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 reg [(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; |
|
reg [(LGMEM-1):0] raddr; |
reg [(BUSW-1):0] mem[0:((1<<LGMEM)-1)]; |
|
// Our status/config register |
wire bw_reset_request, bw_manual_trigger, |
bw_disable_trigger, bw_reset_complete; |
reg [22:0] br_config; |
wire [19:0] bw_holdoff; |
initial br_config = ((1<<(LGMEM-1))-4); |
always @(posedge i_wb_clk) |
if ((i_wb_stb)&&(~i_wb_addr)) |
begin |
if (i_wb_we) |
br_config <= { i_wb_data[31], |
(i_wb_data[27]), |
i_wb_data[26], |
i_wb_data[19:0] }; |
end else if (bw_reset_complete) |
br_config[22] <= 1'b1; |
assign bw_reset_request = (~br_config[22]); |
assign bw_manual_trigger = (br_config[21]); |
assign bw_disable_trigger = (br_config[20]); |
assign bw_holdoff = br_config[19:0]; |
|
wire dw_reset, dw_manual_trigger, dw_disable_trigger; |
generate |
if (SYNCHRONOUS > 0) |
begin |
assign dw_reset = bw_reset_request; |
assign dw_manual_trigger = bw_manual_trigger; |
assign dw_disable_trigger = bw_disable_trigger; |
assign bw_reset_complete = bw_reset_request; |
end else begin |
reg r_reset_complete; |
reg [2:0] r_iflags, q_iflags; |
|
// Resets are synchronous to the bus clock, not the data clock |
// so do a clock transfer here |
initial q_iflags = 3'b000; |
initial r_reset_complete = 1'b0; |
always @(posedge i_clk) |
begin |
q_iflags <= { bw_reset_request, bw_manual_trigger, bw_disable_trigger }; |
r_iflags <= q_iflags; |
r_reset_complete <= (dw_reset); |
end |
|
assign dw_reset = r_iflags[2]; |
assign dw_manual_trigger = r_iflags[1]; |
assign dw_disable_trigger = r_iflags[0]; |
|
reg q_reset_complete, qq_reset_complete; |
// Pass an acknowledgement back from the data clock to the bus |
// clock that the reset has been accomplished |
initial q_reset_complete = 1'b0; |
initial qq_reset_complete = 1'b0; |
always @(posedge i_wb_clk) |
begin |
q_reset_complete <= r_reset_complete; |
qq_reset_complete <= q_reset_complete; |
end |
|
assign bw_reset_complete = qq_reset_complete; |
end endgenerate |
|
// |
// Set up the trigger |
// |
// |
// Write with the i-clk, or input clock. All outputs read with the |
// WISHBONE-clk, or i_wb_clk clock. |
reg dr_triggered, dr_primed; |
wire dw_trigger; |
assign dw_trigger = (dr_primed)&&( |
((i_trigger)&&(~dw_disable_trigger)) |
||(dr_triggered) |
||(dw_manual_trigger)); |
initial dr_triggered = 1'b0; |
always @(posedge i_clk) |
if (dw_reset) |
dr_triggered <= 1'b0; |
else if ((i_ce)&&(dw_trigger)) |
dr_triggered <= 1'b1; |
|
// |
// Determine when memory is full and capture is complete |
// |
// Writes take place on the data clock |
reg dr_stopped, dr_past_holdoff; |
reg [19:0] counter; // This is unsigned |
initial dr_stopped = 1'b0; |
initial counter = 20'h0000; |
initial dr_past_holdoff = 1'b0; |
always @(posedge i_clk) |
dr_past_holdoff <= (counter >= bw_holdoff); |
always @(posedge i_clk) |
if (dw_reset) |
begin |
counter <= 0; |
dr_stopped <= 1'b0; |
end else if ((i_ce)&&(dr_triggered)) |
begin // MUST BE a < and not <=, so that we can keep this w/in |
// 20 bits. Else we'd need to add a bit to comparison |
// here. |
if (~dr_stopped) |
counter <= counter + 20'h01; |
dr_stopped <= (dr_stopped)||(dr_past_holdoff); |
end |
|
// |
// Actually do our writes to memory. Record, via 'primed' when |
// the memory is full. |
// |
// The 'waddr' address that we are using really crosses two clock |
// domains. While writing and changing, it's in the data clock |
// domain. Once stopped, it becomes part of the bus clock domain. |
// The clock transfer on the stopped line handles the clock |
// transfer for these signals. |
// |
reg [(LGMEM-1):0] waddr; |
initial waddr = {(LGMEM){1'b0}}; |
initial dr_primed = 1'b0; |
always @(posedge i_clk) |
if (dw_reset) // For simulation purposes, supply a valid value |
begin |
waddr <= 0; // upon reset. |
dr_primed <= 1'b0; |
end else if ((i_ce)&&(~dr_stopped)) |
begin |
// mem[waddr] <= i_data; |
waddr <= waddr + {{(LGMEM-1){1'b0}},1'b1}; |
dr_primed <= (dr_primed)||(&waddr); |
end |
always @(posedge i_clk) |
if ((i_ce)&&(~dr_stopped)) |
mem[waddr] <= i_data; |
|
// |
// Clock transfer of the status signals |
// |
wire bw_stopped, bw_triggered, bw_primed; |
generate |
if (SYNCHRONOUS > 0) |
begin |
assign bw_stopped = dr_stopped; |
assign bw_triggered = dr_triggered; |
assign bw_primed = dr_primed; |
end else begin |
// These aren't a problem, since none of these are strobe |
// signals. They goes from low to high, and then stays high |
// for many clocks. Swapping is thus easy--two flip flops to |
// protect against meta-stability and we're done. |
// |
reg [2:0] q_oflags, r_oflags; |
initial q_oflags = 3'h0; |
initial r_oflags = 3'h0; |
always @(posedge i_wb_clk) |
if (bw_reset_request) |
begin |
q_oflags <= 3'h0; |
r_oflags <= 3'h0; |
end else begin |
q_oflags <= { dr_stopped, dr_triggered, dr_primed }; |
r_oflags <= q_oflags; |
end |
|
assign bw_stopped = r_oflags[2]; |
assign bw_triggered = r_oflags[1]; |
assign bw_primed = r_oflags[0]; |
end endgenerate |
|
// Reads use the bus clock |
reg br_wb_ack, r_wb_ack; // takes one clock to read |
wire bw_cyc_stb, bus_read_fifo; |
assign bw_cyc_stb = (i_wb_stb); |
assign bus_read_fifo = (i_wb_stb)&&(i_wb_addr)&&(~i_wb_we); |
initial br_wb_ack = 1'b0; |
always @(posedge i_wb_clk) |
begin // CE depends upon 5 inputs, output on 7 (ignoring add&carries) |
if ((bw_reset_request) |
||((bw_cyc_stb)&&(i_wb_addr)&&(i_wb_we))) |
raddr <= 0; |
else if ((bus_read_fifo)&&(bw_stopped)) |
raddr <= raddr + {{(LGMEM-1){1'b0}},1'b1}; // Data read, when stopped |
|
r_wb_ack <= i_wb_stb; |
br_wb_ack <= r_wb_ack; |
end |
|
reg [(LGMEM-1):0] nxt_addr; |
always @(posedge i_wb_clk) // 2 adds, then 5 inputs |
if (bus_read_fifo) |
nxt_addr <= nxt_addr + {{(LGMEM-1){1'b0}},1'b1}; |
else |
nxt_addr <= raddr + waddr; |
// nxt_addr <= raddr + waddr + (bus_read_fifo) |
// ? {{(LGMEM-1){1'b0}},1'b1}: 0; |
|
reg [31:0] nxt_mem; |
always @(posedge i_wb_clk) |
nxt_mem <= mem[nxt_addr]; |
|
reg r_wb_addr; |
always @(posedge i_clk) |
r_wb_addr <= i_wb_addr; |
|
wire [4:0] bw_lgmem; |
assign bw_lgmem = LGMEM; |
always @(posedge i_wb_clk) |
if (~r_wb_addr) // Control register read |
o_wb_data <= { bw_reset_request, |
bw_stopped, |
bw_triggered, |
bw_primed, |
bw_manual_trigger, |
bw_disable_trigger, |
(raddr == {(LGMEM){1'b0}}), |
bw_lgmem, |
bw_holdoff }; |
else if (~bw_stopped) // read, prior to stopping |
o_wb_data <= i_data; |
else // if (i_wb_addr) // Read from FIFO memory |
o_wb_data <= nxt_mem; // mem[raddr+waddr]; |
|
assign o_wb_stall = 1'b0; |
assign o_wb_ack = (br_wb_ack); |
|
reg br_level_interrupt; |
initial br_level_interrupt = 1'b0; |
assign o_interrupt = (bw_stopped)&&(~bw_disable_trigger) |
&&(~br_level_interrupt); |
always @(posedge i_wb_clk) |
if ((bw_reset_complete)||(bw_reset_request)) |
br_level_interrupt<= 1'b0; |
else |
br_level_interrupt<= (bw_stopped)&&(~bw_disable_trigger); |
|
endmodule |
/rtl/wbicapetwo.v
0,0 → 1,262
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbicapetwo.v |
// |
// Project: Wishbone to ICAPE2 interface conversion |
// |
// Purpose: This routine maps the configuration registers of a 7-series |
// Xilinx part onto register addresses on a wishbone bus interface |
// via the ICAPE2 access port to those parts. The big thing this |
// captures is the timing and handshaking required to read and |
// write registers from the configuration interface. |
// |
// As an example of what can be done, writing a 32'h00f to |
// local address 5'h4 sends the IPROG command to the FPGA, causing |
// it to immediately reconfigure itself. |
// |
// As another example, the warm boot start address is located |
// in register 5'h10. Writing to this address, followed by |
// issuing the IPROG command just mentioned will cause the |
// FPGA to configure from that warm boot start address. |
// |
// For more details on the configuration interface, the registers |
// in question, their meanings and what they do, please see |
// User's Guide 470, the "7 Series FPGAs Configuration" User |
// Guide. |
// |
// Notes: This module supports both reads and writes from the ICAPE2 |
// interface. These follow the following pattern. |
// |
// For writes: |
// (Idle) 0xffffffff (Dummy) |
// (CS/W) 0x20000000 NOOP |
// (CS/W) 0xaa995566 SYNC WORD |
// (CS/W) 0x20000000 NOOP |
// (CS/W) 0x20000000 NOOP |
// (CS/W) ... Write command |
// (CS/W) ... Write value, from Wishbone bus |
// (CS/W) 0x20000000 NOOP |
// (CS/W) 0x20000000 NOOP |
// (CS/W) 0x30008001 Write to CMD register (address 4) |
// (CS/W) 0x0000000d DESYNC command |
// (CS/W) 0x20000000 NOOP |
// (CS/W) 0x20000000 NOOP |
// (Idle) |
// |
// and for reads: |
// (Idle) 0xffffffff (Dummy) |
// (CS/W) 0x20000000 NOOP |
// (CS/W) 0xaa995566 SYNC WORD |
// (CS/W) 0x20000000 NOOP |
// (CS/W) 0x20000000 NOOP |
// (CS/W) ... Read command |
// (CS/W) 0x20000000 NOOP |
// (CS/W) 0x20000000 NOOP |
// (Idle) 0x20000000 (Idle the interface again, so we can rd) |
// (CS/R) 0x20000000 (Wait) |
// (CS/R) 0x20000000 (Wait) |
// (CS/R) 0x20000000 (Wait) |
// (CS/R) 0x20000000 (Wait) |
// (Idle) 0x20000000 (Idle the interface before writing) |
// (CS/W) 0x20000000 NOOP |
// (CS/W) 0x20000000 NOOP |
// (CS/W) 0x30008001 Write to CMD register (address 4) |
// (CS/W) 0x0000000d DESYNC command |
// (CS/W) 0x20000000 NOOP |
// (CS/W) 0x20000000 NOOP |
// (Idle) |
// 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. |
// |
// License: GPL, v3, as defined and found on www.gnu.org, |
// http://www.gnu.org/licenses/gpl.html |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
`define MBOOT_IDLE 5'h00 |
`define MBOOT_START 5'h01 |
`define MBOOT_READ 5'h06 |
`define MBOOT_WRITE 5'h0f |
`define MBOOT_DESYNC 5'h11 |
module wbicapetwo(i_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); |
input i_clk; |
// Wishbone inputs |
input i_wb_cyc, i_wb_stb, i_wb_we; |
input [4:0] i_wb_addr; |
input [31:0] i_wb_data; |
// Wishbone outputs |
output reg o_wb_ack, o_wb_stall; |
output reg [31:0] o_wb_data; |
// ICAPE2 interface signals |
// These are kept internal to this block ... |
|
reg wb_req, r_we; |
reg [31:0] r_data; |
reg [4:0] r_addr; |
|
reg slow_clk; |
reg [31:0] cfg_in; |
reg cfg_cs_n, cfg_rdwrn; |
wire [31:0] cfg_out; |
reg [4:0] state; |
initial state = `MBOOT_IDLE; |
initial cfg_cs_n = 1'b1; |
always @(posedge i_clk) |
begin |
o_wb_ack <= 1'b0; |
// Turn any request "off", so that it will not be ack'd, if |
// the wb_cyc line is ever lowered. |
wb_req <= wb_req & i_wb_cyc; |
slow_clk <= slow_clk ^ 1'b1; |
o_wb_stall <= (state != `MBOOT_IDLE)&&(slow_clk); |
if (~slow_clk) |
begin |
state <= state + 5'h01; |
case(state) |
`MBOOT_IDLE: begin |
cfg_cs_n <= 1'b1; |
cfg_rdwrn <= 1'b1; |
cfg_in <= 32'hffffffff; // Dummy word |
|
state <= `MBOOT_IDLE; |
|
o_wb_ack <= 1'b0; |
|
r_addr <= i_wb_addr; |
r_data <= i_wb_data; |
r_we <= i_wb_we; |
if(i_wb_stb) |
begin |
state <= `MBOOT_START; |
wb_req <= 1'b1; |
// |
o_wb_ack <= 1'b0; |
end end |
`MBOOT_START: cfg_in <= 32'hffffffff; // NOOP |
5'h02: begin |
cfg_cs_n <= 1'b0; // Activate interface |
cfg_rdwrn <= 1'b0; |
cfg_in <= 32'h20000000; // NOOP |
end |
5'h03: cfg_in <= 32'haa995566; // Sync word |
5'h04: cfg_in <= 32'h20000000; // NOOP |
5'h05: begin |
cfg_in <= 32'h20000000; // NOOP |
state <= (r_we) ? `MBOOT_WRITE : `MBOOT_READ; |
end |
`MBOOT_READ: cfg_in <= { 8'h28, 6'h0, r_addr, 13'h001 }; |
5'h07: cfg_in <= 32'h20000000; // NOOP |
5'h08: cfg_in <= 32'h20000000; // NOOP |
5'h09: begin // Idle the interface before the read cycle |
cfg_cs_n <= 1'b1; |
cfg_rdwrn <= 1'b1; |
cfg_in <= 32'h20000000; // NOOP |
end |
5'h0a: begin // Re-activate the interface and wait 3 cycles |
cfg_cs_n <= 1'b0; |
cfg_rdwrn <= 1'b1; |
cfg_in <= 32'h20000000; // NOOP |
end |
5'h0b: // ... still waiting, cycle two |
cfg_in <= 32'h20000000; // NOOP |
5'h0c: // ... still waiting, cycle three |
cfg_in <= 32'h20000000; // NOOP |
5'h0d: // ... still waiting, cycle four |
cfg_in <= 32'h20000000; // NOOP |
5'h0e: begin // and now our answer is there |
cfg_cs_n <= 1'b1; |
cfg_rdwrn <= 1'b1; |
cfg_in <= 32'h20000000; // NOOP |
// |
// Wishbone return |
o_wb_ack <= wb_req; |
o_wb_data <= cfg_out; |
wb_req <= 1'b0; |
// |
state <= `MBOOT_DESYNC; |
end |
`MBOOT_WRITE: // Issue a write command to the given address |
cfg_in <= { 8'h30, 6'h0, r_addr, 13'h001 }; |
5'h10: cfg_in <= r_data; // Write the value |
`MBOOT_DESYNC: begin |
cfg_cs_n <= 1'b0; |
cfg_rdwrn <= 1'b0; |
cfg_in <= 32'h20000000; // 1st NOOP |
end |
5'h12: cfg_in <= 32'h20000000; // 2nd NOOP |
5'h13: cfg_in <= 32'h30008001; // Write to CMD register |
5'h14: cfg_in <= 32'h0000000d; // DESYNC command |
5'h15: cfg_in <= 32'h20000000; // NOOP |
5'h16: cfg_in <= 32'h20000000; // NOOP |
5'h17: begin |
// Acknowledge the bus transaction, it is now complete |
o_wb_ack <= wb_req; |
wb_req <= 1'b0; |
// |
cfg_cs_n <= 1'b1; |
cfg_rdwrn <= 1'b0; |
cfg_in <= 32'hffffffff; // DUMMY |
// |
state <= `MBOOT_IDLE; |
end |
default: begin |
o_wb_ack <= 1'b0; |
cfg_cs_n <= 1'b1; |
cfg_rdwrn <= 1'b0; |
state <= `MBOOT_IDLE; |
cfg_in <= 32'hffffffff; // DUMMY WORD |
end |
endcase |
end else begin |
o_wb_ack <= 1'b0; |
end |
end |
|
genvar k; |
// |
// The data registers to the ICAPE2 interface are bit swapped within |
// each byte. Thus, in order to read from or write to the interface, |
// we need to bit swap the bits in each byte. These next lines |
// accomplish that for both the input and output ports. |
// |
wire [31:0] bit_swapped_cfg_in; |
generate |
for(k=0; k<8; k=k+1) |
begin |
assign bit_swapped_cfg_in[ k] = cfg_in[ 7-k]; |
assign bit_swapped_cfg_in[ 8+k] = cfg_in[ 8+7-k]; |
assign bit_swapped_cfg_in[16+k] = cfg_in[16+7-k]; |
assign bit_swapped_cfg_in[24+k] = cfg_in[24+7-k]; |
end endgenerate |
|
wire [31:0] bit_swapped_cfg_out; |
generate |
for(k=0; k<8; k=k+1) |
begin |
assign cfg_out[ k] = bit_swapped_cfg_out[ 7-k]; |
assign cfg_out[ 8+k] = bit_swapped_cfg_out[ 8+7-k]; |
assign cfg_out[16+k] = bit_swapped_cfg_out[16+7-k]; |
assign cfg_out[24+k] = bit_swapped_cfg_out[24+7-k]; |
end endgenerate |
|
ICAPE2 #(.ICAP_WIDTH("X32")) reconfig(.CLK(slow_clk), |
.CSIB(cfg_cs_n), .RDWRB(cfg_rdwrn), |
.I(bit_swapped_cfg_in), .O(bit_swapped_cfg_out)); |
endmodule |
/rtl/busmaster.v
0,0 → 1,1076
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: busmaster.v |
// |
// Project: OpenArty, an entirely open SoC based upon the Arty platform |
// |
// Purpose: This is the "bus interconnect", herein called the "busmaster". |
// This module connects all the devices on the Wishbone bus |
// within this project together. It is created by hand, not |
// automatically. |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
`define NO_ZIP_WBU_DELAY |
`define ZIPCPU |
`ifdef ZIPCPU |
`define ZIP_SYSTEM |
`ifndef ZIP_SYSTEM |
`define ZIP_BONES |
`endif // ZIP_SYSTEM |
`endif // ZipCPU |
// |
// |
`define SDCARD_ACCESS |
`define ETHERNET_ACCESS |
`ifndef VERILATOR |
`define ICAPE_ACCESS |
`endif |
`define FLASH_ACCESS |
//`define SDRAM_ACCESS |
`define GPS_CLOCK |
// UART_ACCESS and GPS_UART have both been placed within fastio |
// `define UART_ACCESS |
// `define GPS_UART |
`define RTC_ACCESS |
`define OLEDRGB_ACCESS |
// |
// `define CPU_SCOPE |
// `define GPS_SCOPE |
`define FLASH_SCOPE |
// `define SDRAM_SCOPE |
// `define ENET_SCOPE |
// |
// |
module busmaster(i_clk, i_rst, |
// CNC |
i_rx_stb, i_rx_data, o_tx_stb, o_tx_data, i_tx_busy, |
// Boad I/O |
i_sw, i_btn, o_led, |
o_clr_led0, o_clr_led1, o_clr_led2, o_clr_led3, |
// PMod I/O |
i_aux_rx, o_aux_tx, o_aux_cts, i_gps_rx, o_gps_tx, |
// The Quad SPI Flash |
o_qspi_cs_n, o_qspi_sck, o_qspi_dat, i_qspi_dat, o_qspi_mod, |
// The DDR3 SDRAM |
o_ddr_reset_n, o_ddr_cke, |
o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n, |
o_ddr_dqs, o_ddr_addr, o_ddr_ba, o_ddr_data, i_ddr_data, |
// The SD Card |
o_sd_sck, o_sd_cmd, o_sd_data, i_sd_cmd, i_sd_data, i_sd_detect, |
// Ethernet control (MDIO) lines |
o_mdclk, o_mdio, o_mdwe, i_mdio, |
// OLED Control interface (roughly SPI) |
o_oled_sck, o_oled_cs_n, o_oled_mosi, o_oled_dcn, |
o_oled_reset_n, o_oled_vccen, o_oled_pmoden, |
// The GPS PMod |
i_gps_pps, i_gps_3df |
); |
parameter ZA=24, ZIPINTS=13; |
input i_clk, i_rst; |
// The bus commander, via an external uart port |
input i_rx_stb; |
input [7:0] i_rx_data; |
output wire o_tx_stb; |
output wire [7:0] o_tx_data; |
input i_tx_busy; |
// I/O to/from board level devices |
input [3:0] i_sw; // 16 switch bus |
input [3:0] i_btn; // 5 Buttons |
output wire [3:0] o_led; // 16 wide LED's |
output wire [2:0] o_clr_led0, o_clr_led1, o_clr_led2, o_clr_led3; |
// PMod UARTs |
input i_aux_rx; |
output wire o_aux_tx, o_aux_cts; |
input i_gps_rx; |
output wire o_gps_tx; |
// Quad-SPI flash control |
output wire o_qspi_cs_n, o_qspi_sck; |
output wire [3:0] o_qspi_dat; |
input [3:0] i_qspi_dat; |
output wire [1:0] o_qspi_mod; |
// DDR3 RAM controller |
output wire o_ddr_reset_n, o_ddr_cke, |
o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n,o_ddr_we_n; |
output wire [2:0] o_ddr_dqs; |
output wire [13:0] o_ddr_addr; |
output wire [2:0] o_ddr_ba; |
output wire [31:0] o_ddr_data; |
input [31:0] i_ddr_data; |
// The SD Card |
output wire o_sd_sck; |
output wire o_sd_cmd; |
output wire [3:0] o_sd_data; |
input i_sd_cmd; |
input [3:0] i_sd_data; |
input i_sd_detect; |
// Ethernet control (MDIO) |
output wire o_mdclk, o_mdio, o_mdwe; |
input i_mdio; |
// OLEDRGB interface |
output wire o_oled_sck, o_oled_cs_n, o_oled_mosi, |
o_oled_dcn, o_oled_reset_n, o_oled_vccen, |
o_oled_pmoden; |
// GPS PMod (GPS UART above) |
input i_gps_pps; |
input i_gps_3df; |
|
// |
// |
// Master wishbone wires |
// |
// |
wire wb_cyc, wb_stb, wb_we, wb_stall, wb_err; |
wire [31:0] wb_data, wb_addr; |
reg wb_ack; |
reg [31:0] wb_idata; |
|
// Interrupts |
wire gpio_int, oled_int, flash_int, scop_int; |
wire enet_tx_int, enet_rx_int, sdcard_int, rtc_int, rtc_pps, |
auxrx_int, auxtx_int, gpsrx_int, sw_int, btn_int; |
|
// |
// |
// First BUS master source: The UART |
// |
// |
wire [31:0] dwb_idata; |
|
// Wires going to devices |
wire wbu_cyc, wbu_stb, wbu_we; |
wire [31:0] wbu_addr, wbu_data; |
// and then coming from devices |
wire wbu_ack, wbu_stall, wbu_err; |
wire [31:0] wbu_idata; |
// And then headed back home |
wire w_interrupt; |
// Oh, and the debug control for the ZIP CPU |
wire wbu_zip_sel, zip_dbg_ack, zip_dbg_stall; |
wire [31:0] zip_dbg_data; |
wbubus genbus(i_clk, i_rx_stb, i_rx_data, |
wbu_cyc, wbu_stb, wbu_we, wbu_addr, wbu_data, |
(wbu_zip_sel)?zip_dbg_ack:wbu_ack, |
(wbu_zip_sel)?zip_dbg_stall:wbu_stall, |
wbu_err, |
(wbu_zip_sel)?zip_dbg_data:wbu_idata, |
w_interrupt, |
o_tx_stb, o_tx_data, i_tx_busy); |
|
// assign o_dbg = (wbu_ack)&&(wbu_cyc); |
|
wire zip_cpu_int; // True if the CPU suddenly halts |
`ifdef ZIPCPU |
// Are we trying to access the ZipCPU? Such accesses must be special, |
// because they must succeed regardless of whether or not the ZipCPU |
// is on the bus. Hence, we trap them here. |
assign wbu_zip_sel = (wbu_addr[27]); |
|
// |
// |
// Second BUS master source: The ZipCPU |
// |
// |
wire zip_cyc, zip_stb, zip_we; |
wire [(ZA-1):0] w_zip_addr; |
wire [31:0] zip_data, zip_scope_data; |
// and then coming from devices |
wire zip_ack, zip_stall, zip_err; |
|
`ifdef ZIP_SYSTEM |
wire [(ZIPINTS-1):0] zip_interrupt_vec = { |
// Lazy(ier) interrupts |
oled_int, gpio_int, rtc_int, scop_int, flash_int, sw_int, btn_int, |
// Fast interrupts |
sdcard_int, auxtx_int, auxrx_int, enet_tx_int, enet_rx_int, |
gpsrx_int, rtc_pps |
}; |
|
zipsystem #( .RESET_ADDRESS(24'h08000), |
.ADDRESS_WIDTH(ZA), |
.LGICACHE(10), |
.START_HALTED(1), |
.EXTERNAL_INTERRUPTS(ZIPINTS), |
.HIGHSPEED_CPU(0)) |
zippy(i_clk, i_rst, |
// Zippys wishbone interface |
zip_cyc, zip_stb, zip_we, w_zip_addr, zip_data, |
zip_ack, zip_stall, dwb_idata, zip_err, |
zip_interrupt_vec, zip_cpu_int, |
// Debug wishbone interface |
((wbu_cyc)&&(wbu_zip_sel)), |
((wbu_stb)&&(wbu_zip_sel)),wbu_we, wbu_addr[0], |
wbu_data, |
zip_dbg_ack, zip_dbg_stall, zip_dbg_data |
`ifdef CPU_DEBUG |
, zip_scope_data |
`endif |
); |
`else // ZIP_SYSTEM |
wire w_zip_cpu_int_ignored; |
zipbones #( .RESET_ADDRESS(24'h08000), |
.ADDRESS_WIDTH(ZA), |
.LGICACHE(10), |
.START_HALTED(1), |
.HIGHSPEED_CPU(0)) |
zippy(i_clk, i_rst, |
// Zippys wishbone interface |
zip_cyc, zip_stb, zip_we, w_zip_addr, zip_data, |
zip_ack, zip_stall, dwb_idata, zip_err, |
w_interrupt, w_zip_cpu_int_ignored, |
// Debug wishbone interface |
((wbu_cyc)&&(wbu_zip_sel)), |
((wbu_stb)&&(wbu_zip_sel)),wbu_we, wbu_addr[0], |
wbu_data, |
zip_dbg_ack, zip_dbg_stall, zip_dbg_data |
`ifdef CPU_DEBUG |
, zip_scope_data |
`endif |
); |
assign zip_cpu_int = 1'b0; |
`endif // ZIP_SYSTEM v ZIP_BONES |
|
wire [31:0] zip_addr; |
generate |
if (ZA < 32) |
assign zip_addr = { {(32-ZA){1'b0}}, w_zip_addr}; |
else |
assign zip_addr = w_zip_addr; |
endgenerate |
|
// |
// |
// And an arbiter to decide who gets to access the bus |
// |
// |
wire dwb_we, dwb_stb, dwb_cyc, dwb_ack, dwb_stall, dwb_err; |
wire [31:0] dwb_addr, dwb_odata; |
wbpriarbiter #(32,32) wbu_zip_arbiter(i_clk, |
// The ZIP CPU Master -- Gets the priority slot |
zip_cyc, zip_stb, zip_we, zip_addr, zip_data, |
zip_ack, zip_stall, zip_err, |
// The UART interface Master |
(wbu_cyc)&&(~wbu_zip_sel), (wbu_stb)&&(~wbu_zip_sel), wbu_we, |
wbu_addr, wbu_data, |
wbu_ack, wbu_stall, wbu_err, |
// Common bus returns |
dwb_cyc, dwb_stb, dwb_we, dwb_addr, dwb_odata, |
dwb_ack, dwb_stall, dwb_err); |
|
// |
// |
// And because the ZIP CPU and the Arbiter create an unacceptable |
// delay, we fail timing. So we add in a delay cycle ... |
// |
// |
assign wbu_idata = dwb_idata; |
busdelay wbu_zip_delay(i_clk, |
dwb_cyc, dwb_stb, dwb_we, dwb_addr, dwb_odata, |
dwb_ack, dwb_stall, dwb_idata, dwb_err, |
wb_cyc, wb_stb, wb_we, wb_addr, wb_data, |
wb_ack, wb_stall, wb_idata, wb_err); |
|
`else // ZIPCPU |
assign zip_cpu_int = 1'b0; // No CPU here to halt |
assign wbu_zip_sel = 1'b0; |
|
// If there's no ZipCPU, there's no need for a Zip/WB-Uart bus delay. |
// We can go directly from the WB-Uart master bus to the master bus |
// itself. |
assign wb_cyc = wbu_cyc; |
assign wb_stb = wbu_stb; |
assign wb_we = wbu_we; |
assign wb_addr = wbu_addr; |
assign wb_data = wbu_data; |
assign wbu_idata = wb_idata; |
assign wbu_ack = wb_ack; |
assign wbu_stall = wb_stall; |
assign wbu_err = wb_err; |
|
// The CPU never halts if it doesn't exist, so set this interrupt to |
// zero. |
assign zip_cpu_int= 1'b0; |
`endif // ZIPCPU |
|
|
// |
// Peripheral select lines. |
// |
// These lines will be true during any wishbone cycle whose address |
// line selects the given I/O peripheral. The none_sel and many_sel |
// lines are used to detect problems, such as when no device is |
// selected or many devices are selected. Such problems will lead to |
// bus errors (below). |
// |
wire io_sel, scop_sel, netb_sel, |
flctl_sel, rtc_sel, sdcard_sel, netp_sel, |
oled_sel, gps_sel, mio_sel, cfg_sel, |
mem_sel, flash_sel, ram_sel, |
none_sel, many_sel; |
|
wire [4:0] skipaddr; |
assign skipaddr = { wb_addr[26], wb_addr[22], wb_addr[15], wb_addr[11], |
~wb_addr[8] }; |
assign ram_sel = (skipaddr[4]); |
assign flash_sel = (skipaddr[4:3]==2'b01); |
assign mem_sel = (skipaddr[4:2]==3'b001); |
assign netb_sel = (skipaddr[4:1]==4'b0001); |
assign io_sel = (~|skipaddr)&&(wb_addr[7:5]==3'b000); |
assign scop_sel = (~|skipaddr)&&(wb_addr[7:3]==5'b00100); |
assign rtc_sel = (~|skipaddr)&&(wb_addr[7:2]==6'b001010); |
assign sdcard_sel= (~|skipaddr)&&(wb_addr[7:2]==6'b001011); |
assign netp_sel = (~|skipaddr)&&(wb_addr[7:2]==6'b001101); |
assign oled_sel = (~|skipaddr)&&(wb_addr[7:2]==6'b001110); |
assign gps_sel = (~|skipaddr)&&( (wb_addr[7:2]==6'b001100) |
|| (wb_addr[7:3]==5'b01000)); |
assign mio_sel = (~|skipaddr)&&(wb_addr[7:5]==3'b101); |
assign flctl_sel = (~|skipaddr)&&(wb_addr[7:5]==3'b110); |
assign cfg_sel = (~|skipaddr)&&(wb_addr[7:5]==3'b111); |
|
wire skiperr; |
assign skiperr = (|wb_addr[31:27]) |
||(~skipaddr[4])&&(|wb_addr[25:23]) |
||(skipaddr[4:3]==2'b00)&&(|wb_addr[21:16]) |
||(skipaddr[4:2]==3'b000)&&(|wb_addr[14:12]) |
||(skipaddr[4:1]==4'b0000)&&(|wb_addr[10:9]); |
|
|
// |
// Peripheral acknowledgement lines |
// |
// These are only a touch more confusing, since the flash device will |
// ACK for both flctl_sel (the control line select), as well as the |
// flash_sel (the memory line select). Hence we have one fewer ack |
// line. |
wire io_ack, oled_ack, |
rtc_ack, sdcard_ack, |
netp_ack, gps_ack, mio_ack, cfg_ack, netb_ack, |
mem_ack, flash_ack, ram_ack; |
reg many_ack, slow_many_ack; |
reg slow_ack, scop_ack; |
wire [4:0] ack_list; |
assign ack_list = { ram_ack, flash_ack, mem_ack, netb_ack, cfg_ack }; |
initial many_ack = 1'b0; |
always @(posedge i_clk) |
many_ack <= ((ack_list != 5'h10) |
&&(ack_list != 5'h8) |
&&(ack_list != 5'h4) |
&&(ack_list != 5'h2) |
&&(ack_list != 5'h1) |
&&(ack_list != 5'h0)); |
/* |
assign many_ack = ( { 2'h0, ram_ack} |
+{2'h0, flash_ack } |
+{2'h0, mem_ack } |
+{2'h0, netb_ack } |
+{2'h0, slow_ack } > 3'h1 ); |
*/ |
|
wire [7:0] slow_ack_list; |
assign slow_ack_list = { mio_ack, gps_ack, netp_ack, |
sdcard_ack, rtc_ack, scop_ack, oled_ack, io_ack }; |
initial slow_many_ack = 1'b0; |
always @(posedge i_clk) |
slow_many_ack <= ((slow_ack_list != 8'h80) |
&&(slow_ack_list != 8'h40) |
&&(slow_ack_list != 8'h20) |
&&(slow_ack_list != 8'h10) |
&&(slow_ack_list != 8'h08) |
&&(slow_ack_list != 8'h04) |
&&(slow_ack_list != 8'h02) |
&&(slow_ack_list != 8'h01) |
&&(slow_ack_list != 8'h00)); |
|
always @(posedge i_clk) |
wb_ack <= (wb_cyc)&&(|{ ram_ack, flash_ack, mem_ack, |
netb_ack, cfg_ack, slow_ack }); |
always @(posedge i_clk) |
slow_ack <= (wb_cyc)&&(|{oled_ack, mio_ack, gps_ack, |
netp_ack, sdcard_ack, rtc_ack, scop_ack, |
oled_ack, io_ack}); |
|
// |
// Peripheral data lines |
// |
wire [31:0] io_data, oled_data, |
rtc_data, sdcard_data, |
netp_data, gps_data, mio_data, cfg_data, netb_data, |
mem_data, flash_data, ram_data; |
reg [31:0] slow_data, scop_data; |
|
// 4 control lines, 5x32 data lines ... |
always @(posedge i_clk) |
if ((ram_ack)||(flash_ack)) |
wb_idata <= (ram_ack)?ram_data:flash_data; |
else if ((mem_ack)||(netb_ack)) |
wb_idata <= (mem_ack)?mem_data:netb_data; |
else |
wb_idata <= slow_data; |
|
// 7 control lines, 8x32 data lines |
always @(posedge i_clk) |
if ((cfg_ack)||(mio_ack)) |
slow_data <= (cfg_ack) ? cfg_data : mio_data; |
else if ((gps_ack)||(netp_ack)) |
slow_data <= (gps_ack) ? gps_data : netp_data; |
else if ((sdcard_ack)||(rtc_ack)) |
slow_data <= (sdcard_ack)?sdcard_data : rtc_data; |
else if ((scop_ack)|(oled_ack)) |
slow_data <= (scop_ack)?scop_data:oled_data; |
else |
slow_data <= io_data; |
|
// |
// Peripheral stall lines |
// |
// As per the wishbone spec, these cannot be clocked or delayed. They |
// *must* be done via combinatorial logic. |
// |
wire io_stall, scop_stall, oled_stall, |
rtc_stall, sdcard_stall, |
netp_stall, gps_stall, mio_stall, cfg_stall, netb_stall, |
mem_stall, flash_stall, ram_stall, |
many_stall; |
assign wb_stall = (wb_cyc)&&( |
((io_sel)&&(io_stall)) // Never stalls |
||((scop_sel)&&(scop_stall)) // Never stalls |
||((rtc_sel)&&(rtc_stall)) // Never stalls |
||((sdcard_sel)&&(sdcard_stall))// Never stalls |
||((netp_sel)&&(netp_stall)) |
||((gps_sel)&&(gps_stall)) //(maybe? never stalls?) |
||((oled_sel)&&(oled_stall)) |
||((mio_sel)&&(mio_stall)) |
||((cfg_sel)&&(cfg_stall)) |
||((netb_sel)&&(netb_stall)) // Never stalls |
||((mem_sel)&&(mem_stall)) // Never stalls |
||((flash_sel|flctl_sel)&&(flash_stall)) |
||((ram_sel)&&(ram_stall))); |
|
|
// |
// Bus Error calculation(s) |
// |
|
// Selecting nothing is only an error if the strobe line is high as well |
// as the cycle line. However, this is captured within the wb_err |
// logic itself, so we can ignore it for a line or two. |
assign none_sel = ( //(skiperr)|| |
(~|{ io_sel, scop_sel, flctl_sel, rtc_sel, |
sdcard_sel, netp_sel, gps_sel, |
oled_sel, |
mio_sel, cfg_sel, netb_sel, mem_sel, |
flash_sel,ram_sel })); |
// |
// Selecting multiple devices at once is a design flaw that should |
// never happen. Hence, if this logic won't build, we won't include |
// it. Still, having this logic in place has saved my tush more than |
// once. |
// |
reg [31:0] sel_addr; |
always @(posedge i_clk) |
sel_addr <= wb_addr; |
|
reg many_sel_a, many_sel_b, single_sel_a, single_sel_b, last_stb; |
always @(posedge i_clk) |
begin |
last_stb <= wb_stb; |
|
single_sel_a <= (wb_stb)&&((ram_sel)|(flash_sel) |
|(mem_sel)|(netb_sel)|(cfg_sel)); |
many_sel_a <= 1'b0; |
if ((ram_sel)&&((flash_sel)||(mem_sel)||(netb_sel)||cfg_sel)) |
many_sel_a <= 1'b1; |
else if ((flash_sel)&&((mem_sel)||(netb_sel)||cfg_sel)) |
many_sel_a <= 1'b1; |
else if ((mem_sel)&&((netb_sel)||cfg_sel)) |
many_sel_a <= 1'b1; |
else if ((netb_sel)&&(cfg_sel)) |
many_sel_a <= 1'b1; |
|
single_sel_b <= (wb_stb)&&((mio_sel)||(gps_sel)||(netp_sel) |
||(sdcard_sel)||(rtc_sel)||(flctl_sel) |
||(oled_sel)||(scop_sel)||(io_sel)); |
many_sel_b <= 1'b0; |
if ((mio_sel)&&((gps_sel)||(netp_sel)||(sdcard_sel)||(rtc_sel) |
||(flctl_sel)||(scop_sel)||(oled_sel)||(io_sel))) |
many_sel_b <= 1'b1; |
else if ((gps_sel)&&((netp_sel)||(sdcard_sel)||(rtc_sel) |
||(flctl_sel)||(scop_sel)||(oled_sel)||(io_sel))) |
many_sel_b <= 1'b1; |
else if ((netp_sel)&&((sdcard_sel)||(rtc_sel) |
||(flctl_sel)||(scop_sel)||(oled_sel)||(io_sel))) |
many_sel_b <= 1'b1; |
else if ((sdcard_sel)&&((rtc_sel) |
||(flctl_sel)||(scop_sel)||(oled_sel)||(io_sel))) |
many_sel_b <= 1'b1; |
else if ((rtc_sel)&&((flctl_sel)||(scop_sel)||(oled_sel)||(io_sel))) |
many_sel_b <= 1'b1; |
else if ((flctl_sel)&&((scop_sel)||(oled_sel)||(io_sel))) |
many_sel_b <= 1'b1; |
else if ((scop_sel)&&((oled_sel)||(io_sel))) |
many_sel_b <= 1'b1; |
else if ((oled_sel)&&(io_sel)) |
many_sel_b <= 1'b1; |
end |
|
wire sel_err; // 5 inputs |
assign sel_err = ( (last_stb)&&(~single_sel_a)&&(~single_sel_b)) |
||((single_sel_a)&&(single_sel_b)) |
||((single_sel_a)&&(many_sel_a)) |
||((single_sel_b)&&(many_sel_b)); |
assign wb_err = (wb_cyc)&&(sel_err || many_ack || slow_many_ack); |
|
|
// Finally, if we ever encounter a bus error, knowing the address of |
// the error will be important to figuring out how to fix it. Hence, |
// we grab it here. Be aware, however, that this might not truly be |
// the address that caused an error: in the case of none_sel it will |
// be, but if many_ack or slow_many_ack are true then we might just be |
// looking at an address on the bus that was nearby the one requested. |
reg [31:0] bus_err_addr; |
initial bus_err_addr = 32'h00; |
always @(posedge i_clk) |
if (wb_err) |
bus_err_addr <= sel_addr; |
|
// |
// I/O peripheral |
// |
// The I/O processor, herein called an fastio. This is a unique |
// set of peripherals--these are all of the peripherals that can answer |
// in a single clock--or, rather, they are the peripherals that can |
// answer the bus before their clock. Hence, the fastio simply consists |
// of a mux that selects between various peripheral responses. Further, |
// these peripherals are not allowed to stall the bus. |
// |
// There is no option for turning these off--they will always be on. |
wire [8:0] master_ints; |
assign master_ints = { zip_cpu_int, oled_int, rtc_int, sdcard_int, |
enet_tx_int, enet_rx_int, |
scop_int, flash_int, rtc_pps }; |
wire [5:0] board_ints; |
wire [3:0] w_led; |
wire rtc_ppd; |
fastio #( |
.AUXUART_SETUP(30'hd50), |
.GPSUART_SETUP(30'hd20833) |
) runio(i_clk, i_sw, i_btn, |
w_led, o_clr_led0, o_clr_led1, o_clr_led2, o_clr_led3, |
i_aux_rx, o_aux_tx, o_aux_cts, i_gps_rx, o_gps_tx, |
wb_cyc, (io_sel)&&(wb_stb), wb_we, wb_addr[4:0], |
wb_data, io_ack, io_stall, io_data, |
rtc_ppd, |
bus_err_addr, master_ints, w_interrupt, |
board_ints); |
assign { gpio_int, auxrx_int, auxtx_int, gpsrx_int, sw_int, btn_int } = board_ints; |
|
/* |
reg [25:0] dbg_counter_err, dbg_counter_cyc, dbg_counter_sel, |
dbg_counter_many; |
// assign wb_err = (wb_cyc)&&(sel_err || many_ack || slow_many_ack); |
always @(posedge i_clk) |
if (wbu_cyc) |
dbg_counter_cyc <= 0; |
else if (!dbg_counter_cyc[25]) |
dbg_counter_cyc <= dbg_counter_cyc+26'h1; |
always @(posedge i_clk) |
if (wbu_err) |
dbg_counter_err <= 0; |
else if (!dbg_counter_err[25]) |
dbg_counter_err <= dbg_counter_err+26'h1; |
always @(posedge i_clk) |
if ((wb_cyc)&&(sel_err)) |
dbg_counter_sel <= 0; |
else if (!dbg_counter_sel[25]) |
dbg_counter_sel <= dbg_counter_sel+26'h1; |
always @(posedge i_clk) |
if ((wb_cyc)&&(many_ack)) |
dbg_counter_many <= 0; |
else if (!dbg_counter_many[25]) |
dbg_counter_many <= dbg_counter_many+26'h1; |
assign o_led = { |
(!dbg_counter_many[25])|w_led[3], |
(!dbg_counter_sel[25])|w_led[2], |
(!dbg_counter_cyc[25])|w_led[1], |
(!dbg_counter_err[25])|w_led[0] }; |
*/ |
assign o_led = w_led; |
|
|
// |
// |
// Real Time Clock (RTC) device level access |
// |
// |
wire gps_tracking, ck_pps; |
wire [63:0] gps_step; |
`ifdef RTC_ACCESS |
rtcgps #(32'h15798f) // 2^48 / 200MHz |
thertc(i_clk, |
wb_cyc, (wb_stb)&&(rtc_sel), wb_we, |
wb_addr[1:0], wb_data, |
rtc_data, rtc_int, rtc_ppd, |
gps_tracking, ck_pps, gps_step[47:16], rtc_pps); |
`else |
assign rtc_data = 32'h00; |
assign rtc_int = 1'b0; |
assign rtc_pps = 1'b0; |
assign rtc_ppd = 1'b0; |
`endif |
reg r_rtc_ack; |
initial r_rtc_ack = 1'b0; |
always @(posedge i_clk) |
r_rtc_ack <= (wb_stb)&&(rtc_sel); |
assign rtc_ack = r_rtc_ack; |
assign rtc_stall = 1'b0; |
|
// |
// |
// SDCard device level access |
// |
// |
`ifdef SDCARD_ACCESS |
wire [31:0] sd_dbg; |
// SPI mapping |
wire w_sd_cs_n, w_sd_mosi, w_sd_miso; |
|
sdspi sdctrl(i_clk, |
wb_cyc, (wb_stb)&&(sdcard_sel), wb_we, |
wb_addr[1:0], wb_data, |
sdcard_ack, sdcard_stall, sdcard_data, |
w_sd_cs_n, o_sd_sck, w_sd_mosi, w_sd_miso, |
sdcard_int, 1'b1, sd_dbg); |
assign w_sd_miso = i_sd_data[0]; |
assign o_sd_data = { w_sd_cs_n, 3'b111 }; |
assign o_sd_cmd = w_sd_mosi; |
`else |
reg r_sdcard_ack; |
always @(posedge i_clk) |
r_sdcard_ack <= (wb_stb)&&(sdcard_sel); |
assign sdcard_ack = r_sdcard_ack; |
|
assign sdcard_data = 32'h00; |
assign sdcard_stall= 1'b0; |
assign sdcard_int = 1'b0; |
`endif |
|
// |
// |
// OLEDrgb device control |
// |
// |
`ifdef OLEDRGB_ACCESS |
wboled rgbctrl(i_clk, |
wb_cyc, (wb_stb)&&(oled_sel), wb_we, |
wb_addr[1:0], wb_data, |
oled_ack, oled_stall, oled_data, |
o_oled_sck, o_oled_cs_n, o_oled_mosi, o_oled_dcn, |
{ o_oled_reset_n, o_oled_vccen, o_oled_pmoden }, |
oled_int); |
`else |
assign o_oled_cs_n = 1'b1; |
assign o_oled_sck = 1'b1; |
assign o_oled_mosi = 1'b1; |
assign o_oled_dcn = 1'b1; |
assign o_oled_reset_n = 1'b0; |
assign o_oled_vccen = 1'b0; |
assign o_oled_pmoden = 1'b0; |
|
reg r_oled_ack; |
always @(posedge i_clk) |
r_oled_ack <= (wb_stb)&&(oled_sel); |
assign oled_ack = r_oled_ack; |
|
assign oled_data = 32'h00; |
assign oled_stall= 1'b0; |
assign oled_int = 1'b0; |
`endif |
|
// |
// |
// GPS CLOCK CONTROLS, BOTH THE TEST BENCH AND THE CLOCK ITSELF |
// |
// |
wire [63:0] gps_now, gps_err; |
wire [31:0] gck_data, gtb_data; |
wire gck_ack, gck_stall, gtb_ack, gtb_stall; |
`ifdef GPS_CLOCK |
// |
// GPS CLOCK SCHOOL TESTING |
// |
wire gps_pps, tb_pps, gps_locked; |
wire [1:0] gps_dbg_tick; |
|
gpsclock_tb ppscktb(i_clk, ck_pps, tb_pps, |
(wb_stb)&&(gps_sel)&&(wb_addr[3]), |
wb_we, wb_addr[2:0], |
wb_data, gtb_ack, gtb_stall, gtb_data, |
gps_err, gps_now, gps_step); |
`ifdef GPSTB |
assign gps_pps = tb_pps; // Let the truth come from our test bench |
`else |
assign gps_pps = i_gps_pps; |
`endif |
wire gps_led; |
|
// |
// GPS CLOCK CONTROL |
// |
gpsclock ppsck(i_clk, 1'b0, gps_pps, ck_pps, gps_led, |
(wb_stb)&&(gps_sel)&&(~wb_addr[3]), |
wb_we, wb_addr[1:0], |
wb_data, gck_ack, gck_stall, gck_data, |
gps_tracking, gps_now, gps_step, gps_err, gps_locked, |
gps_dbg_tick); |
`else |
|
assign gps_err = 64'h0; |
assign gps_now = 64'h0; |
assign gck_data = 32'h0; |
assign gtb_data = 32'h0; |
assign gtb_stall = 1'b0; |
assign gck_stall = 1'b0; |
assign ck_pps = 1'b0; |
|
assign gps_tracking = 1'b0; |
// Appropriate step for a 200MHz clock |
assign gps_step = { 16'h00, 32'h015798e, 16'h00 }; |
|
reg r_gck_ack; |
always @(posedge i_clk) |
r_gck_ack <= (wb_stb)&&(gps_sel); |
assign gck_ack = r_gck_ack; |
assign gtb_ack = r_gck_ack; |
|
`endif |
|
assign gps_ack = (gck_ack | gtb_ack); |
assign gps_stall = (gck_stall | gtb_stall); |
assign gps_data = (gck_ack) ? gck_data : gtb_data; |
|
|
// |
// ETHERNET DEVICE ACCESS |
// |
`ifdef ETHERNET_ACCESS |
reg r_mio_ack, r_netb_ack, r_netp_ack; |
always @(posedge i_clk) |
r_mio_ack <= (wb_stb)&&(mio_sel); |
always @(posedge i_clk) |
r_netp_ack <= (wb_stb)&&(netp_sel); |
assign mio_ack = r_mio_ack; |
assign netp_ack = r_netp_ack; |
|
assign mio_data = 32'h00; |
assign netp_data = 32'h00; |
assign mio_stall = 1'b0; |
assign netp_stall= 1'b0; |
assign enet_rx_int = 1'b0; |
assign enet_tx_int = 1'b0; |
|
enetctrl #(3) |
mdio(i_clk, i_rst, wb_cyc, (wb_stb)&&(netb_sel), wb_we, |
wb_addr[4:0], wb_data[15:0], |
netb_ack, netb_stall, netb_data, |
o_mdclk, o_mdio, i_mdio, o_mdwe); |
`else |
reg r_mio_ack, r_netb_ack, r_netp_ack; |
always @(posedge i_clk) |
r_mio_ack <= (wb_stb)&&(mio_sel); |
always @(posedge i_clk) |
r_netp_ack <= (wb_stb)&&(netp_sel); |
assign mio_ack = r_mio_ack; |
assign netp_ack = r_netp_ack; |
|
assign mio_data = 32'h00; |
assign netp_data = 32'h00; |
assign mio_stall = 1'b0; |
assign netp_stall= 1'b0; |
assign enet_rx_int = 1'b0; |
assign enet_tx_int = 1'b0; |
|
// |
// 2kW memory, 1kW for each of transmit and receive. (Max pkt length |
// is 512W, so this allows for two 512W in memory.) Since we don't |
// really have ethernet without ETHERNET_ACCESS defined, this just |
// consumes resources for us so we have an idea of what might be |
// available when we do have ETHERNET_ACCESS defined. |
// |
memdev #(11) enet_buffers(i_clk, wb_cyc, (wb_stb)&&(netb_sel), wb_we, |
wb_addr[10:0], wb_data, netb_ack, netb_stall, netb_data); |
assign o_mdclk = 1'b1; |
assign o_mdio = 1'b1; |
assign o_mdwe = 1'b1; |
|
`endif |
|
|
// |
// MULTIBOOT/ICAPE2 CONFIGURATION ACCESS |
// |
`ifdef ICAPE_ACCESS |
wbicapetwo fpga_cfg(i_clk, wb_cyc,(cfg_sel)&&(wb_stb), wb_we, |
wb_addr[4:0], wb_data, |
cfg_ack, cfg_stall, cfg_data); |
`else |
reg r_cfg_ack; |
always @(posedge i_clk) |
r_cfg_ack <= (cfg_sel)&&(wb_stb); |
assign cfg_ack = r_cfg_ack; |
assign cfg_stall = 1'b0; |
assign cfg_data = 32'h00; |
`endif |
|
// |
// RAM MEMORY ACCESS |
// |
// There is no option to turn this off--this RAM must always be |
// present in the design. |
memdev #(15) // 32kW, or 128kB, 15 address lines |
blkram(i_clk, wb_cyc, (wb_stb)&&(mem_sel), wb_we, wb_addr[14:0], |
wb_data, mem_ack, mem_stall, mem_data); |
|
// |
// FLASH MEMORY ACCESS |
// |
`ifdef FLASH_ACCESS |
`ifdef FLASH_SCOPE |
wire [31:0] flash_debug; |
`endif |
wire w_ignore_cmd_accepted; |
eqspiflash flashmem(i_clk, i_rst, |
wb_cyc,(wb_stb)&&(flash_sel),(wb_stb)&&(flctl_sel),wb_we, |
wb_addr[21:0], wb_data, |
flash_ack, flash_stall, flash_data, |
o_qspi_sck, o_qspi_cs_n, o_qspi_mod, o_qspi_dat, i_qspi_dat, |
flash_int, w_ignore_cmd_accepted |
`ifdef FLASH_SCOPE |
, flash_debug |
`endif |
); |
`else |
assign o_qspi_sck = 1'b1; |
assign o_qspi_cs_n= 1'b1; |
assign o_qspi_mod = 2'b01; |
assign o_qspi_dat = 4'h0; |
assign flash_data = 32'h00; |
assign flash_stall = 1'b0; |
assign flash_int = 1'b0; |
|
reg r_flash_ack; |
always @(posedge i_clk) |
r_flash_ack <= (wb_stb)&&(flash_sel); |
assign flash_ack = r_flash_ack; |
`endif |
|
|
// |
// |
// DDR3-SDRAM |
// |
// |
`ifdef SDRAM_ACCESS |
wbddrsdram rami(i_clk, |
wb_cyc, (wb_stb)&&(ram_sel), wb_we, wb_addr[25:0], wb_data, |
ram_ack, ram_stall, ram_data, |
o_ddr_reset_n, o_ddr_cke, |
o_ddr_cs_n, o_ddr_ras_n, o_ddr_cas_n, o_ddr_we_n, |
o_ddr_dqs, |
o_ddr_addr, o_ddr_ba, o_ddr_data, i_ddr_data); |
`else |
assign ram_data = 32'h00; |
assign ram_stall = 1'b0; |
reg r_ram_ack; |
always @(posedge i_clk) |
r_ram_ack <= (wb_stb)&&(ram_sel); |
assign ram_ack = r_ram_ack; |
|
// And idle the DDR3 SDRAM |
assign o_ddr_reset_n = 1'b0; // Leave the SDRAM in reset |
assign o_ddr_cke = 1'b0; // Disable the SDRAM clock |
// DQS |
assign o_ddr_dqs = 3'b100; // Leave DQS pins in high impedence |
// DDR3 control wires (not enabled if CKE=0) |
assign o_ddr_cs_n = 1'b0; // NOOP command |
assign o_ddr_ras_n = 1'b1; |
assign o_ddr_cas_n = 1'b1; |
assign o_ddr_we_n = 1'b1; |
// (Unused) data wires |
assign o_ddr_addr = 14'h00; |
assign o_ddr_ba = 3'h0; |
assign o_ddr_data = 32'h00; |
`endif |
|
|
// |
// |
// WISHBONE SCOPES |
// |
// |
// |
// |
wire [31:0] scop_a_data; |
wire scop_a_ack, scop_a_stall, scop_a_interrupt; |
`ifdef CPU_SCOPE |
wire [31:0] scop_cpu_data; |
wire scop_cpu_ack, scop_cpu_stall, scop_cpu_interrupt; |
wire scop_cpu_trigger; |
// assign scop_cpu_trigger = zip_scope_data[30]; |
assign scop_cpu_trigger = (wb_stb)&&(mem_sel)&&(~wb_we) |
&&(wb_err)||(zip_scope_data[31]); |
wbscope #(5'd13) cpuscope(i_clk, 1'b1,(scop_cpu_trigger), zip_scope_data, |
// Wishbone interface |
i_clk, wb_cyc, ((wb_stb)&&(scop_sel)&&(wb_addr[2:1]==2'b00)), wb_we, wb_addr[0], |
wb_data, |
scop_cpu_ack, scop_cpu_stall, scop_cpu_data, |
scop_cpu_interrupt); |
|
assign scop_a_data = scop_cpu_data; |
assign scop_a_ack = scop_cpu_ack; |
assign scop_a_stall = scop_cpu_stall; |
assign scop_a_interrupt = scop_cpu_interrupt; |
`else |
`ifdef FLASH_SCOPE |
wire [31:0] scop_flash_data; |
wire scop_flash_ack, scop_flash_stall, scop_flash_interrupt; |
wire scop_flash_trigger; |
// assign scop_cpu_trigger = zip_scope_data[30]; |
assign scop_flash_trigger = (wb_stb)&&((flash_sel)||(flctl_sel)); |
wbscope #(5'd13) flashscope(i_clk, 1'b1, |
(scop_flash_trigger), flash_debug, |
// Wishbone interface |
i_clk, wb_cyc, ((wb_stb)&&(scop_sel)&&(wb_addr[2:1]==2'b00)), wb_we, wb_addr[0], |
wb_data, |
scop_flash_ack, scop_flash_stall, scop_flash_data, |
scop_flash_interrupt); |
|
assign scop_a_data = scop_flash_data; |
assign scop_a_ack = scop_flash_ack; |
assign scop_a_stall = scop_flash_stall; |
assign scop_a_interrupt = scop_flash_interrupt; |
`else |
reg r_scop_a_ack; |
always @(posedge i_clk) |
r_scop_a_ack <= (wb_stb)&&(scop_sel)&&(wb_addr[2:1] == 2'b00); |
assign scop_a_data = 32'h00; |
assign scop_a_ack = r_scop_a_ack; |
assign scop_a_stall = 1'b0; |
assign scop_a_interrupt = 1'b0; |
`endif |
`endif |
|
wire [31:0] scop_b_data; |
wire scop_b_ack, scop_b_stall, scop_b_interrupt; |
`ifdef GPS_SCOPE |
reg [18:0] r_gps_debug; |
wire [31:0] scop_gps_data; |
wire scop_gps_ack, scop_gps_stall, scop_gps_interrupt; |
always @(posedge i_clk) |
r_gps_debug <= { |
gps_dbg_tick, gps_tracking, gps_locked, |
gpu_data[7:0], |
// (wb_cyc)&&(wb_stb)&&(io_sel), |
(wb_stb)&&(io_sel)&&(wb_addr[4:3]==2'b11)&&(wb_we), |
(wb_stb)&&(gps_sel)&&(wb_addr[3:2]==2'b01), |
gpu_int, |
i_gps_rx, rtc_pps, ck_pps, i_gps_pps }; |
wbscopc #(5'd13,19,32,1) gpsscope(i_clk, 1'b1, ck_pps, r_gps_debug, |
// Wishbone interface |
i_clk, wb_cyc, ((wb_stb)&&(scop_sel)&&(wb_addr[2:1]==2'b01)), |
wb_we, wb_addr[0], wb_data, |
scop_gps_ack, scop_gps_stall, scop_gps_data, |
scop_gps_interrupt); |
`else |
assign scop_b_data = 32'h00; |
assign scop_b_stall = 1'b0; |
assign scop_b_interrupt = 1'b0; |
|
reg r_scop_b_ack; |
always @(posedge i_clk) |
r_scop_b_ack <= (wb_stb)&&(scop_sel)&&(wb_addr[2:1] == 2'b01); |
assign scop_b_ack = r_scop_b_ack; |
`endif |
|
// |
// SCOPE C |
// |
wire [31:0] scop_c_data; |
wire scop_c_ack, scop_c_stall, scop_c_interrupt; |
// |
//`else |
assign scop_c_data = 32'h00; |
assign scop_c_stall = 1'b0; |
assign scop_c_interrupt = 1'b0; |
|
reg r_scop_c_ack; |
always @(posedge i_clk) |
r_scop_c_ack <= (wb_stb)&&(scop_sel)&&(wb_addr[2:1] == 2'b10); |
assign scop_c_ack = r_scop_c_ack; |
//`endif |
|
// |
// SCOPE D |
// |
wire [31:0] scop_d_data; |
wire scop_d_ack, scop_d_stall, scop_d_interrupt; |
// |
//`else |
assign scop_d_data = 32'h00; |
assign scop_d_stall = 1'b0; |
assign scop_d_interrupt = 1'b0; |
|
reg r_scop_d_ack; |
always @(posedge i_clk) |
r_scop_d_ack <= (wb_stb)&&(scop_sel)&&(wb_addr[2:1] == 2'b11); |
assign scop_d_ack = r_scop_d_ack; |
//`endif |
|
assign scop_int = scop_a_interrupt |
|| scop_b_interrupt |
|| scop_c_interrupt |
|| scop_d_interrupt; |
assign scop_stall = ((wb_addr[2:1]==2'b0)?scop_a_stall |
: ((wb_addr[2:1]==2'b01)?scop_b_stall |
: ((wb_addr[2:1]==2'b11)?scop_c_stall |
: scop_d_stall))); // Will always be 1'b0; |
initial scop_ack = 1'b0; |
always @(posedge i_clk) |
scop_ack <= scop_a_ack | scop_b_ack | scop_c_ack | scop_d_ack; |
always @(posedge i_clk) |
if (scop_a_ack) |
scop_data <= scop_a_data; |
else if (scop_b_ack) |
scop_data <= scop_b_data; |
else if (scop_c_ack) |
scop_data <= scop_c_data; |
else // if (scop_d_ack) |
scop_data <= scop_d_data; |
|
endmodule |
/rtl/wbuidleint.v
0,0 → 1,125
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbuidleint.v |
// |
// Project: FPGA library |
// |
// Purpose: Creates an output for the interface, inserting idle words and |
// words indicating an interrupt has taken place into the output |
// stream. Henceforth, the output means more than just bus transaction |
// results. It may mean there is no bus transaction result to report, |
// or that an interrupt has taken place. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
module wbuidleint(i_clk, i_stb, i_codword, i_cyc, i_busy, i_int, |
o_stb, o_codword, o_busy, |
i_tx_busy); |
input i_clk; |
// From the FIFO following the bus executor |
input i_stb; |
input [35:0] i_codword; |
// From the rest of the board |
input i_cyc, i_busy, i_int; |
// To the next stage |
output reg o_stb; |
output reg [35:0] o_codword; |
output reg o_busy; |
// Is the next stage busy? |
input i_tx_busy; |
|
reg int_request, int_sent; |
initial int_request = 1'b0; |
always @(posedge i_clk) |
if((o_stb)&&(~i_tx_busy)&&(o_codword[35:30]==6'h4)) |
int_request <= i_int; |
else |
int_request <= (int_request)||(i_int); |
|
|
// Now, for the idle counter |
wire idle_expired; |
reg idle_state; |
reg [35:0] idle_counter; |
initial idle_counter = 36'h0000; |
always @(posedge i_clk) |
if ((i_stb)||(o_stb)) |
idle_counter <= 36'h000; |
else if (~idle_counter[35]) |
idle_counter <= idle_counter + 36'd43; |
|
initial idle_state = 1'b0; |
always @(posedge i_clk) |
if ((o_stb)&&(~i_tx_busy)&&(o_codword[35:31]==5'h0)) |
idle_state <= 1'b1; |
else if (~idle_counter[35]) |
idle_state <= 1'b0; |
|
assign idle_expired = (~idle_state)&&(idle_counter[35]); |
|
initial o_stb = 1'b0; |
initial o_busy = 1'b0; |
always @(posedge i_clk) |
if ((o_stb)&&(i_tx_busy)) |
begin |
o_busy <= 1'b1; |
end else if (o_stb) // and not i_tx_busy |
begin |
// Idle one clock before becoming not busy |
o_stb <= 1'b0; |
o_busy <= 1'b1; |
end else if (o_busy) |
o_busy <= 1'b0; |
else if (i_stb) // and (~o_busy)&&(~o_stb) |
begin // On a valid output, just send it out |
// We'll open this strobe, even if the transmitter |
// is busy, just 'cause we might otherwise lose it |
o_codword <= i_codword; |
o_stb <= 1'b1; |
o_busy <= 1'b1; |
end else if ((int_request)&&(~int_sent)) |
begin |
o_stb <= 1'b1; |
o_codword <= { 6'h4, 30'h0000 }; // interrupt codeword |
o_busy <= 1'b1; |
end else if (idle_expired) |
begin // Strobe, if we're not writing or our |
// last command wasn't an idle |
o_stb <= 1'b1; |
o_busy <= 1'b1; |
if (i_cyc) |
o_codword <= { 6'h1, 30'h0000 }; // idle codeword, bus busy |
else |
o_codword <= { 6'h0, 30'h0000 }; |
end |
|
initial int_sent = 1'b0; |
always @(posedge i_clk) |
if ((int_request)&&((~o_stb)&&(~o_busy)&&(~i_stb))) |
int_sent <= 1'b1; |
else if (~i_int) |
int_sent <= 1'b0; |
endmodule |
/rtl/wbucompress.v
0,0 → 1,283
//////////////////////////////////////////////////////////////////////////////// |
// |
// Filename: wbucompress.v |
// |
// Project: FPGA library |
// |
// Purpose: When reading many words that are identical, it makes no sense |
// to spend the time transmitting the same thing over and over |
// again, especially on a slow channel. Hence this routine uses a table |
// lookup to see if the word to be transmitted was one from the recent |
// past. If so, the word is replaced with an address of the recently |
// transmitted word. Mind you, the table lookup takes one clock per table |
// entry, so even if a word is in the table it might not be found in time. |
// If the word is not in the table, or if it isn't found due to a lack of |
// time, the word is placed into the table while incrementing every other |
// table address. |
// |
// Oh, and on a new address--the table is reset and starts over. This way, |
// any time the host software changes, the host software will always start |
// by issuing a new address--hence the table is reset for every new piece |
// of software that may wish to communicate. |
// |
// |
// Creator: Dan Gisselquist, Ph.D. |
// Gisselquist Technology, LLC |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
//////////////////////////////////////////////////////////////////////////////// |
// |
// |
// All input words are valid codewords. If we can, we make them |
// better here. |
module wbucompress(i_clk, i_stb, i_codword, o_stb, o_cword, i_busy); |
parameter DW=32, CW=36, TBITS=10; |
input i_clk, i_stb; |
input [(CW-1):0] i_codword; |
output wire o_stb; |
output wire [(CW-1):0] o_cword; |
input i_busy; |
|
// |
// |
// First stage is to compress the address. |
// This stage requires one clock. |
// |
// ISTB,ICODWORD |
// ISTB2,IWRD2 ASTB,AWORD |
// ISTB3,IWRD3 ASTB2,AWRD2 I_BUSY(1) |
// ISTB3,IWRD3 ASTB2,AWRD2 I_BUSY(1) |
// ISTB3,IWRD3 ASTB2,AWRD2 I_BUSY(1) |
// ISTB3,IWRD3 ASTB2,AWRD2 |
// ISTB4,IWRD4 ASTB3,AWRD3 I_BUSY(2) |
// ISTB4,IWRD4 ASTB3,AWRD3 I_BUSY(2) |
// ISTB4,IWRD4 ASTB3,AWRD3 I_BUSY(2) |
reg a_stb; |
reg [35:0] a_addrword; |
wire [31:0] w_addr; |
assign w_addr = i_codword[31:0]; |
always @(posedge i_clk) |
if ((i_stb)&&(~a_stb)) |
begin |
if (i_codword[35:32] != 4'h2) |
begin |
a_addrword <= i_codword; |
end else if (w_addr[31:6] == 26'h00) |
a_addrword <= { 6'hc, w_addr[ 5:0], 24'h00 }; |
else if (w_addr[31:12] == 20'h00) |
a_addrword <= { 6'hd, w_addr[11:0], 18'h00 }; |
else if (w_addr[31:18] == 14'h00) |
a_addrword <= { 6'he, w_addr[17:0], 12'h00 }; |
else if (w_addr[31:24] == 8'h00) |
a_addrword <= { 6'hf, w_addr[23:0], 6'h00 }; |
else begin |
a_addrword <= i_codword; |
end |
end |
initial a_stb = 1'b0; |
always @(posedge i_clk) |
if ((i_stb)&&(~a_stb)) |
a_stb <= i_stb; |
else if (~i_busy) |
a_stb <= 1'b0; |
|
|
// |
// |
// The next stage attempts to replace data codewords with previous |
// codewords that may have been sent. The memory is only allowed |
// to be as old as the last new address command. In this fashion, |
// any program that wishes to talk to the device can start with a |
// known compression table by simply setting the address and then |
// reading from the device. |
// |
|
// We start over any time a new value shows up, and |
// the follow-on isn't busy and can take it. Likewise, |
// we reset the writer on the compression any time a |
// i_clr value comes through (i.e., ~i_cyc or new |
// address) |
|
wire w_accepted; |
assign w_accepted = (a_stb)&&(~i_busy); |
|
reg r_stb; |
always @(posedge i_clk) |
r_stb <= a_stb; |
|
wire [35:0] r_word; |
assign r_word = a_addrword; |
|
|
// |
// First step of the compression is keeping track of a compression |
// table. And the first part of that is keeping track of what address |
// to write into the compression table, and whether or not the entire |
// table is full or not. This logic follows: |
// |
reg [(TBITS-1):0] tbl_addr; |
reg tbl_filled; |
// First part, write the compression table |
always @(posedge i_clk) |
// If we send a new address, then reset the table to empty |
if (w_accepted) |
begin |
// Reset on new address (0010xx) and on new compressed |
// addresses (0011ll). |
if (o_cword[35:33]==3'h1) |
tbl_addr <= 0; |
// Otherwise, on any valid return result that wasn't |
// from our table, for whatever reason (such as didn't |
// have the clocks to find it, etc.), increment the |
// address to add another value into our table |
else if (o_cword[35:33] == 3'b111) |
tbl_addr <= tbl_addr + {{(TBITS-1){1'b0}},1'b1}; |
end |
always @(posedge i_clk) |
if ((w_accepted)&&(o_cword[35:33]==3'h1)) // on new address |
tbl_filled <= 1'b0; |
else if (tbl_addr == 10'h3ff) |
tbl_filled <= 1'b1; |
|
// Now that we know where we are writing into the table, and what |
// values of the table are valid, we need to actually write into |
// the table. |
// |
// We can keep this logic really simple by writing on every clock |
// and writing junk on many of those clocks, but we'll need to remember |
// that the value of the table at tbl_addr is unreliable until tbl_addr |
// changes. |
// |
reg [31:0] compression_tbl [0:((1<<TBITS)-1)]; |
// Write new values into the table |
always @(posedge i_clk) |
compression_tbl[tbl_addr] <= { r_word[32:31], r_word[29:0] }; |
|
// Now that we have a working table, can we use it? |
// On any new word, we'll start looking through our codewords. |
// If we find any that matches, we're there. We might (or might not) |
// make it through the table first. That's irrelevant. We just look |
// while we can. |
reg tbl_match, nxt_match; // <= (nxt_rd_addr == tbl_addr); |
reg [(TBITS-1):0] rd_addr; |
reg [(TBITS-1):0] nxt_rd_addr; |
initial rd_addr = 0; |
initial tbl_match = 0; |
always @(posedge i_clk) |
begin |
nxt_match <= ((nxt_rd_addr-tbl_addr)=={{(TBITS-1){1'b0}},1'b1}); |
if ((w_accepted)||(~a_stb)) |
begin |
// Keep in mind, if a write was just accepted, then |
// rd_addr will need to be reset on the next clock |
// when (~a_stb). Hence this must be a two clock |
// update |
rd_addr <= tbl_addr + {(TBITS){1'b1}}; |
nxt_rd_addr = tbl_addr + { {(TBITS-1){1'b1}}, 1'b0 }; |
tbl_match <= 1'b0; |
end else if ((~tbl_match)&&(~match) |
&&((~nxt_rd_addr[TBITS-1])||(tbl_filled))) |
begin |
rd_addr <= nxt_rd_addr; |
nxt_rd_addr = nxt_rd_addr - { {(TBITS-1){1'b0}}, 1'b1 }; |
tbl_match <= nxt_match; |
end |
end |
|
reg [1:0] pmatch; |
reg dmatch, // Match, on clock 'd' |
vaddr; // Was the address valid then? |
reg [(DW-1):0] cword; |
reg [(TBITS-1):0] caddr, daddr, maddr; |
always @(posedge i_clk) |
begin |
cword <= compression_tbl[rd_addr]; |
caddr <= rd_addr; |
|
dmatch <= (cword == { r_word[32:31], r_word[29:0] }); |
daddr <= caddr; |
maddr <= tbl_addr - caddr; |
|
vaddr <= ( {1'b0, caddr} < {tbl_filled, tbl_addr} ) |
&&(caddr != tbl_addr); |
end |
|
always @(posedge i_clk) |
if ((w_accepted)||(~a_stb)) |
pmatch <= 0; // rd_addr is set on this clock |
else |
// cword is set on the next clock, pmatch = 3'b001 |
// dmatch is set on the next clock, pmatch = 3'b011 |
pmatch <= { pmatch[0], 1'b1 }; |
|
reg match; |
reg [(TBITS-1):0] matchaddr; |
always @(posedge i_clk) |
if((w_accepted)||(~a_stb)||(~r_stb))// Reset upon any write |
match <= 1'b0; |
else if (~match) |
begin |
// To be a match, the table must not be empty, |
match <= (vaddr)&&(dmatch)&&(r_word[35:33]==3'b111) |
&&(pmatch == 2'b11); |
end |
|
reg zmatch, hmatch, fmatch; |
always @(posedge i_clk) |
if (~match) |
begin |
matchaddr <= maddr; |
fmatch <= (maddr < 10'h521); |
zmatch <= (maddr == 10'h1); |
hmatch <= (maddr < 10'd10); |
end |
|
// Did we find something? |
wire [(TBITS-1):0] adr_diff; |
wire [9:0] adr_dbld; |
wire [2:0] adr_hlfd; |
assign adr_diff = matchaddr; |
assign adr_hlfd = matchaddr[2:0]- 3'd2; |
assign adr_dbld = matchaddr- 10'd10; |
reg [(CW-1):0] r_cword; // Record our result |
always @(posedge i_clk) |
begin |
if ((~a_stb)||(~r_stb)||(w_accepted))//Reset whenever word gets written |
begin |
r_cword <= r_word; |
end else if ((match)&&(fmatch)) // &&(r_word == a_addrword)) |
begin |
r_cword <= r_word; |
if (zmatch) // matchaddr == 1 |
r_cword[35:30] <= { 5'h3, r_word[30] }; |
else if (hmatch) // 2 <= matchaddr <= 9 |
r_cword[35:30] <= { 2'b10, adr_hlfd, r_word[30] }; |
else // if (adr_diff < 10'd521) |
r_cword[35:24] <= { 2'b01, adr_dbld[8:6], |
r_word[30], adr_dbld[5:0] }; |
end else |
r_cword <= r_word; |
end |
|
// Can we do this without a clock delay? |
assign o_stb = a_stb; |
assign o_cword = (r_stb)?(r_cword):(a_addrword); |
endmodule |
|
/rtl/lleqspi.v
0,0 → 1,318
/////////////////////////////////////////////////////////////////////////// |
// |
// Filename: lleqspi.v |
// |
// Project: Wishbone Controlled Quad SPI Flash Controller |
// |
// Purpose: Reads/writes a word (user selectable number of bytes) of data |
// to/from a Quad SPI port. The port is understood to be |
// a normal SPI port unless the driver requests four bit mode. |
// When not in use, unlike our previous SPI work, no bits will |
// toggle. |
// |
// Creator: Dan Gisselquist |
// Gisselquist Technology, LLC |
// |
/////////////////////////////////////////////////////////////////////////// |
// |
// Copyright (C) 2015-2016, Gisselquist Technology, LLC |
// |
// This program is free software (firmware): you can redistribute it and/or |
// modify it under the terms of the GNU General Public License as published |
// 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 |
// |
// |
/////////////////////////////////////////////////////////////////////////// |
`define EQSPI_IDLE 3'h0 |
`define EQSPI_START 3'h1 |
`define EQSPI_BITS 3'h2 |
`define EQSPI_READY 3'h3 |
`define EQSPI_HOLDING 3'h4 |
`define EQSPI_STOP 3'h5 |
`define EQSPI_STOP_B 3'h6 |
`define EQSPI_RECYCLE 3'h7 |
|
// Modes |
`define EQSPI_MOD_SPI 2'b00 |
`define EQSPI_MOD_QOUT 2'b10 // Write |
`define EQSPI_MOD_QIN 2'b11 // Read |
|
module lleqspi(i_clk, |
// Module interface |
i_wr, i_hold, i_word, i_len, i_spd, i_dir, i_recycle, |
o_word, o_valid, o_busy, |
// QSPI interface |
o_sck, o_cs_n, o_mod, o_dat, i_dat); |
input i_clk; |
// Chip interface |
// Can send info |
// i_dir = 1, i_spd = 0, i_hold = 0, i_wr = 1, |
// i_word = { 1'b0, 32'info to send }, |
// i_len = # of bytes in word-1 |
input i_wr, i_hold; |
input [31:0] i_word; |
input [1:0] i_len; // 0=>8bits, 1=>16 bits, 2=>24 bits, 3=>32 bits |
input i_spd; // 0 -> normal QPI, 1 -> QSPI |
input i_dir; // 0 -> read, 1 -> write to SPI |
input i_recycle; // 0 = 20ns, 1 = 50ns |
output reg [31:0] o_word; |
output wire o_valid; |
output reg o_busy; |
// Interface with the QSPI lines |
output reg o_sck; |
output reg o_cs_n; |
output reg [1:0] o_mod; |
output reg [3:0] o_dat; |
input [3:0] i_dat; |
|
// output wire [22:0] o_dbg; |
// assign o_dbg = { state, spi_len, |
// o_busy, o_valid, o_cs_n, o_sck, o_mod, o_dat, i_dat }; |
|
wire i_miso; |
assign i_miso = i_dat[1]; |
|
// These are used in creating a delayed input. |
reg rd_input, rd_spd, rd_valid; |
|
reg r_spd, r_dir; |
reg [3:0] r_recycle; |
reg [5:0] spi_len; |
reg [31:0] r_word; |
reg [30:0] r_input; |
reg [2:0] state; |
initial state = `EQSPI_IDLE; |
initial o_sck = 1'b1; |
initial o_cs_n = 1'b1; |
initial o_dat = 4'hd; |
initial rd_valid = 1'b0; |
initial o_busy = 1'b0; |
initial r_input = 31'h000; |
initial rd_valid = 1'b0; |
always @(posedge i_clk) |
begin |
rd_input <= 1'b0; |
rd_spd <= r_spd; |
rd_valid <= 1'b0; |
|
if ((state == `EQSPI_IDLE)&&(o_sck)) |
begin |
o_cs_n <= 1'b1; |
o_busy <= 1'b0; |
o_mod <= `EQSPI_MOD_SPI; |
r_word <= i_word; |
r_spd <= i_spd; |
r_dir <= i_dir; |
o_dat <= 4'hc; |
r_recycle <= (i_recycle)? 4'h8 : 4'h2; // 4'ha : 4'h4 |
spi_len<= { 1'b0, i_len, 3'b000 } + 6'h8; |
o_sck <= 1'b1; |
if (i_wr) |
begin |
state <= `EQSPI_START; |
o_cs_n <= 1'b0; |
o_busy <= 1'b1; |
end |
end else if (state == `EQSPI_START) |
begin // We come in here with sck high, stay here 'til sck is low |
o_sck <= 1'b0; |
if (o_sck == 1'b0) |
begin |
state <= `EQSPI_BITS; |
spi_len<= spi_len - ( (r_spd)? 6'h4 : 6'h1 ); |
if (r_spd) |
r_word <= { r_word[27:0], 4'h0 }; |
else |
r_word <= { r_word[30:0], 1'b0 }; |
end |
o_mod <= (r_spd) ? { 1'b1, r_dir } : `EQSPI_MOD_SPI; |
o_cs_n <= 1'b0; |
o_busy <= 1'b1; |
if (r_spd) |
o_dat <= r_word[31:28]; |
else |
o_dat <= { 3'b110, r_word[31] }; |
end else if (~o_sck) |
begin |
o_sck <= 1'b1; |
o_busy <= ((state != `EQSPI_READY)||(~i_wr)); |
end else if (state == `EQSPI_BITS) |
begin |
// Should enter into here with at least a spi_len |
// of one, perhaps more |
o_sck <= 1'b0; |
o_busy <= 1'b1; |
if (r_spd) |
begin |
o_dat <= r_word[31:28]; |
r_word <= { r_word[27:0], 4'h0 }; |
spi_len <= spi_len - 6'h4; |
if (spi_len == 6'h4) |
state <= `EQSPI_READY; |
end else begin |
o_dat <= { 3'b110, r_word[31] }; |
r_word <= { r_word[30:0], 1'b0 }; |
spi_len <= spi_len - 6'h1; |
if (spi_len == 6'h1) |
state <= `EQSPI_READY; |
end |
|
rd_input <= 1'b1; |
end else if (state == `EQSPI_READY) |
begin |
o_cs_n <= 1'b0; |
o_busy <= 1'b1; |
// This is the state on the last clock (both low and |
// high clocks) of the data. Data is valid during |
// this state. Here we chose to either STOP or |
// continue and transmit more. |
o_sck <= (i_hold); // No clocks while holding |
if((~o_busy)&&(i_wr))// Acknowledge a new request |
begin |
state <= `EQSPI_BITS; |
o_busy <= 1'b1; |
o_sck <= 1'b0; |
|
// Read the new request off the bus |
r_spd <= i_spd; |
r_dir <= i_dir; |
// Set up the first bits on the bus |
o_mod <= (i_spd) ? { 1'b1, i_dir } : `EQSPI_MOD_SPI; |
if (i_spd) |
begin |
o_dat <= i_word[31:28]; |
r_word <= { i_word[27:0], 4'h0 }; |
// spi_len <= spi_len - 4; |
spi_len<= { 1'b0, i_len, 3'b000 } + 6'h8 |
- 6'h4; |
end else begin |
o_dat <= { 3'b110, i_word[31] }; |
r_word <= { i_word[30:0], 1'b0 }; |
spi_len<= { 1'b0, i_len, 3'b000 } + 6'h8 |
- 6'h1; |
end |
|
// Read a bit upon any transition |
rd_input <= 1'b1; |
rd_valid <= 1'b1; |
end else begin |
o_sck <= 1'b1; |
state <= (i_hold)?`EQSPI_HOLDING : `EQSPI_STOP; |
o_busy <= (~i_hold); |
|
// Read a bit upon any transition |
rd_valid <= 1'b1; |
rd_input <= 1'b1; |
end |
end else if (state == `EQSPI_HOLDING) |
begin |
// We need this state so that the o_valid signal |
// can get strobed with our last result. Otherwise |
// we could just sit in READY waiting for a new command. |
// |
// Incidentally, the change producing this state was |
// the result of a nasty race condition. See the |
// commends in wbqspiflash for more details. |
// |
rd_valid <= 1'b0; |
o_cs_n <= 1'b0; |
o_busy <= 1'b0; |
if((~o_busy)&&(i_wr))// Acknowledge a new request |
begin |
state <= `EQSPI_BITS; |
o_busy <= 1'b1; |
o_sck <= 1'b0; |
|
// Read the new request off the bus |
r_spd <= i_spd; |
r_dir <= i_dir; |
// Set up the first bits on the bus |
o_mod<=(i_spd)?{ 1'b1, i_dir } : `EQSPI_MOD_SPI; |
if (i_spd) |
begin |
o_dat <= i_word[31:28]; |
r_word <= { i_word[27:0], 4'h0 }; |
spi_len<= { 1'b0, i_len, 3'b100 }; |
end else begin |
o_dat <= { 3'b110, i_word[31] }; |
r_word <= { i_word[30:0], 1'b0 }; |
spi_len<= { 1'b0, i_len, 3'b111 }; |
end |
end else begin |
o_sck <= 1'b1; |
state <= (i_hold)?`EQSPI_HOLDING : `EQSPI_STOP; |
o_busy <= (~i_hold); |
end |
end else if (state == `EQSPI_STOP) |
begin |
o_sck <= 1'b1; // Stop the clock |
rd_valid <= 1'b0; // Output may have just been valid, but no more |
o_busy <= 1'b1; // Still busy till port is clear |
state <= `EQSPI_STOP_B; |
// Can't change modes for at least one cycle |
// o_mod <= `EQSPI_MOD_SPI; |
end else if (state == `EQSPI_STOP_B) |
begin |
o_cs_n <= 1'b1; |
o_sck <= 1'b1; |
// Do I need this???? |
// spi_len <= 3; // Minimum CS high time before next cmd |
state <= `EQSPI_RECYCLE; |
o_busy <= 1'b1; |
o_mod <= `EQSPI_MOD_SPI; |
end else begin // Recycle state |
r_recycle <= r_recycle - 1'b1; |
o_cs_n <= 1'b1; |
o_sck <= 1'b1; |
o_busy <= 1'b1; |
o_mod <= `EQSPI_MOD_SPI; |
o_dat <= 4'hc; |
if (r_recycle[3:1] == 3'h0) |
state <= `EQSPI_IDLE; |
end |
/* |
end else begin // Invalid states, should never get here |
state <= `EQSPI_STOP; |
o_valid <= 1'b0; |
o_busy <= 1'b1; |
o_cs_n <= 1'b1; |
o_sck <= 1'b1; |
o_mod <= `EQSPI_MOD_SPI; |
o_dat <= 4'hd; |
end |
*/ |
end |
|
always @(posedge i_clk) |
begin |
if ((state == `EQSPI_IDLE)||(rd_valid)) |
r_input <= 31'h00; |
else if ((rd_input)&&(r_spd)) |
r_input <= { r_input[26:0], i_dat }; |
else if (rd_input) |
r_input <= { r_input[29:0], i_miso }; |
|
if ((rd_valid)&&(r_spd)) |
o_word <= { r_input[27:0], i_dat }; |
else if (rd_valid) |
o_word <= { r_input[30:0], i_miso }; |
end |
|
assign o_valid = rd_valid; |
|
endmodule |
|