blob: 6948fec9000fde24657b12c63b1ed208fd72cbab [file] [log] [blame]
// SPDX-FileCopyrightText: 2020 Efabless Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// SPDX-License-Identifier: Apache-2.0
`default_nettype none
/*
*-------------------------------------------------------------
*
* user_proj_example
*
* This is an example of a (trivially simple) user project,
* showing how the user project can connect to the logic
* analyzer, the wishbone bus, and the I/O pads.
*
* This project generates an integer count, which is output
* on the user area GPIO pads (digital output only). The
* wishbone connection allows the project to be controlled
* (start and stop) from the management SoC program.
*
* See the testbenches in directory "mprj_counter" for the
* example programs that drive this user project. The three
* testbenches are "io_ports", "la_test1", and "la_test2".
*
*-------------------------------------------------------------
*/
module user_proj_example #(
parameter BITS = 32
)(
`ifdef USE_POWER_PINS
inout vccd1, // User area 1 1.8V supply
inout vssd1, // User area 1 digital ground
`endif
// Wishbone Slave ports (WB MI A)
input wb_clk_i,
input wb_rst_i,
input wbs_stb_i,
input wbs_cyc_i,
input wbs_we_i,
input [3:0] wbs_sel_i,
input [31:0] wbs_dat_i,
input [31:0] wbs_adr_i,
output wbs_ack_o,
output [31:0] wbs_dat_o,
// Logic Analyzer Signals
input [127:0] la_data_in,
output [127:0] la_data_out,
input [127:0] la_oenb,
// IOs
input [`MPRJ_IO_PADS-1:0] io_in,
output [`MPRJ_IO_PADS-1:0] io_out,
output [`MPRJ_IO_PADS-1:0] io_oeb,
// IRQ
output [2:0] irq
);
wire clk;
wire rst;
wire [`MPRJ_IO_PADS-1:0] io_in;
wire [`MPRJ_IO_PADS-1:0] io_out;
wire [`MPRJ_IO_PADS-1:0] io_oeb;
// LA
// Assuming LA probes [65:64] are for controlling the count clk & reset
assign clk = (~la_oenb[64]) ? la_data_in[64]: wb_clk_i;
assign rst = (~la_oenb[65]) ? la_data_in[65]: wb_rst_i;
/////////// End clk and rst code //////////
// Data bus switch
wire [31:0] data_out;
wire select;
assign select = la_data_in[112];
assign data_out = select ? la_data_in[63:32] : prng_out;
hyperram hyperram(
.clk(clk),
.rst(rst),
// CPU Interface
.address(la_data_in[31:0]),
//.data_out(la_data_in[63:32]),
.data_out(data_out),
.data_in(la_data_out[31:0]),
.write_enable(la_data_in[66]),
.write_mask(la_data_in[70:67]),
.transaction_begin(la_data_in[71]),
.transaction_end(la_data_out[32]),
// Hyperram Interface
.dq_in(io_in[15:8]),
.dq_out(io_out[15:8]),
.dq_oeb(io_oeb[15:8]),
.ck(io_out[16]),
.ck_bar(io_out[17]),
.cs_bar(io_out[18]),
.rwds_in(io_in[19]),
.rwds_out(io_out[19]),
.rwds_oeb(io_oeb[19]),
// Config
.timed_read(la_data_in[72]),
.WAIT_LATENCY(la_data_in[78:73]),
.DONE_LATENCY(la_data_in[84:79]),
// For testbench
.control_state(la_data_out[102:96])
);
wire [31:0] prng_out;
assign prng_out[31:12] = 0;
acorn_prng acorn_prng(
.clk(clk),
.reset(rst),
.load(la_data_in[85]),
.select(la_data_in[87:86]),
.gpio_seed(la_data_in[99:88]),
.LA1_seed(la_data_in[111:100]),
.out(prng_out[11:0]),
.io_oeb(la_data_out[115:103]),
.reset_out(la_data_out[116])
);
endmodule
module counter #(
parameter BITS = 32
)(
input clk,
input reset,
input valid,
input [3:0] wstrb,
input [BITS-1:0] wdata,
input [BITS-1:0] la_write,
input [BITS-1:0] la_input,
output ready,
output [BITS-1:0] rdata,
output [BITS-1:0] count
);
reg ready;
reg [BITS-1:0] count;
reg [BITS-1:0] rdata;
always @(posedge clk) begin
if (reset) begin
count <= 0;
ready <= 0;
end else begin
ready <= 1'b0;
if (~|la_write) begin
count <= count + 1;
end
if (valid && !ready) begin
ready <= 1'b1;
rdata <= count;
if (wstrb[0]) count[7:0] <= wdata[7:0];
if (wstrb[1]) count[15:8] <= wdata[15:8];
if (wstrb[2]) count[23:16] <= wdata[23:16];
if (wstrb[3]) count[31:24] <= wdata[31:24];
end else if (|la_write) begin
count <= la_write & la_input;
end
end
end
endmodule
module hyperram (
input clk,
input rst,
// CPU Interface
input [31:0] address,
input [31:0] data_out,
output [31:0] data_in,
input write_enable,
input [3:0] write_mask,
input transaction_begin,
output reg transaction_end,
// Hyperram Interface
input [7:0] dq_in,
output [7:0] dq_out,
output [7:0] dq_oeb,
output ck,
output ck_bar,
output cs_bar,
input rwds_in,
output rwds_out,
output rwds_oeb,
// Config
input timed_read,
input [5:0] WAIT_LATENCY,
input [5:0] DONE_LATENCY,
// For testbench
output [6:0] control_state
);
//localparam WAIT_LATENCY = 4;
//localparam DONE_LATENCY = 4;
reg ck_gate;
reg ck;
always @(posedge clk) begin
if(!ck_gate) begin
ck <= 0;
end
else begin
ck <= !ck;
end
end
assign ck_bar = !ck;
reg cs_bar;
reg [7:0] dq_out;
reg dq_oeb;
reg rwds_out;
reg rwds_oeb;
//assign dq = dq_oeb ? 'hz : dq_out;
//assign rwds = rwds_oeb ? 'hz : rwds_out;
wire write_enable_register;
assign write_enable_register = !command_address[47];
assign data_in = data_in_register;
reg [7:0] wait_counter;
reg [7:0] next_wait_counter;
reg [7:0] done_counter;
reg [7:0] next_done_counter;
// FSM
reg [6:0] control_state;
reg [6:0] next_control_state;
reg [31:0] next_data_in;
reg [31:0] data_in_register;
always @(posedge clk) begin
if(rst) begin
control_state <= 0;
data_in_register <= 0;
wait_counter <= 0;
done_counter <= 0;
end
else begin
control_state <= next_control_state;
data_in_register <= next_data_in;
wait_counter <= next_wait_counter;
done_counter <= next_done_counter;
end
end
always @(*) begin
next_control_state = control_state;
cs_bar = 0;
ck_gate = 1;
dq_oeb = 1;
rwds_oeb = 1;
next_data_in = data_in_register;
next_wait_counter = wait_counter;
next_done_counter = done_counter;
dq_out = 'hzz;
rwds_out = 'hz;
transaction_end = 0;
case(control_state)
// IDLE state
'h0 : begin
cs_bar = 1;
ck_gate = 0;
transaction_end = 1;
if(transaction_begin) begin
next_control_state = control_state + 1;
end
end
// CA0 state
'h1 : begin
next_control_state = control_state + 1;
dq_out = command_address[47:40];
dq_oeb = 0;
end
// CA1 state
'h2 : begin
next_control_state = control_state + 1;
dq_out = command_address[39:32];
dq_oeb = 0;
end
// CA2 state
'h3 : begin
next_control_state = control_state + 1;
dq_out = command_address[31:24];
dq_oeb = 0;
end
// CA3 state
'h4 : begin
next_control_state = control_state + 1;
dq_out = command_address[23:16];
dq_oeb = 0;
end
// CA4 state
'h5 : begin
next_control_state = control_state + 1;
dq_out = command_address[15:8];
dq_oeb = 0;
end
// CA5 state
'h6 : begin
next_control_state = control_state + 1;
dq_out = command_address[7:0];
dq_oeb = 0;
end
// WAIT state
'h7 : begin
next_wait_counter = wait_counter + 1;
if(wait_counter == WAIT_LATENCY) begin
next_wait_counter = 0;
if(write_enable_register) begin
next_control_state = 'h8;
end
else begin
next_control_state = 'hc;
end
end
end
// WRITE0 state
'h8 : begin
next_control_state = control_state + 1;
rwds_out = write_mask_register[0];
rwds_oeb = 0;
dq_out = data_out_register[7:0];
dq_oeb = 0;
end
// WRITE1 state
'h9 : begin
next_control_state = control_state + 1;
rwds_out = write_mask_register[1];
rwds_oeb = 0;
dq_out = data_out_register[15:8];
dq_oeb = 0;
end
// WRITE2 state
'ha : begin
next_control_state = control_state + 1;
rwds_out = write_mask_register[2];
rwds_oeb = 0;
dq_out = data_out_register[23:16];
dq_oeb = 0;
end
// WRITE3 state
'hb : begin
next_control_state = 'h10;
rwds_out = write_mask_register[3];
rwds_oeb = 0;
dq_out = data_out_register[31:24];
dq_oeb = 0;
end
// READ0 state - wait for rwds strobe
'hc : begin
if(rwds_in || timed_read) begin
next_data_in[7:0] = dq_in;
next_control_state = control_state + 1;
end
end
// READ1 state
'hd : begin
next_data_in[15:8] = dq_in;
next_control_state = control_state + 1;
end
// READ2 state
'he : begin
next_data_in[23:16] = dq_in;
next_control_state = control_state + 1;
end
// READ3 state
'hf : begin
next_data_in[31:24] = dq_in;
next_control_state = control_state + 1;
end
// DONE state
'h10 : begin
next_done_counter = done_counter + 1;
if(done_counter == DONE_LATENCY) begin
next_control_state = 0;
next_done_counter = 0;
end
end
endcase
end
reg [47:0] command_address;
reg [47:0] next_command_address;
reg [3:0] write_mask_register;
reg [3:0] next_write_mask_register;
reg [31:0] data_out_register;
reg [31:0] next_data_out_register;
always @(posedge transaction_begin) begin
command_address <= next_command_address;
write_mask_register <= next_write_mask_register;
data_out_register <= next_data_out_register;
end
always @(*) begin
next_command_address = {
!write_enable, 1'h0, 1'h1, 9'h000,
address[22:9], address[8:3], 13'h0000, address[2:0]
};
next_write_mask_register = write_mask;
next_data_out_register = data_out;
end
`ifdef FORMAL
reg [5:0] read_count;
reg [5:0] write_count;
always @(posedge clk) begin
if(control_state == 'h0f) begin
read_count <= read_count + 1;
end
if(control_state == 'h0b) begin
write_count <= write_count + 1;
end
end
initial begin
control_state = 'h0;
read_count = 0;
write_count = 0;
rst = 1;
address = 'h12345678;
wait_counter = 0;
done_counter = 0;
data_in = 'haaaabbbb;
data_out = 'hccccdddd;
#10;
rst = 0;
end
always @(posedge clk) begin
cover_idle: cover (control_state == 0);
cover_ca: cover (control_state == 6);
cover_wait: cover (control_state == 7);
cover_write: cover (control_state == 8);
cover_read: cover (control_state == 'hc);
cover_done: cover (control_state == 'h10);
cover_write_2: cover (write_count == 'h2);
cover_read_2: cover (read_count == 'h2);
cover_write_read: cover (read_count == 1 && write_count == 1);
end
`endif
endmodule
//////////////////////////////
// Third-party Code Section //
//////////////////////////////
// Everything below this point is third party code. Included inline to simplify tapeout.
////////////////////////////////////////////////////////////////////////////////////////////
// ACORN (Additive Congruential Random Number) generator, a pseudo random number generator
// Source: https://github.com/ZhenleC/wrapped_acorn_prng/blob/main/acorn_prng/src/acorn_prng.v
// License: https://github.com/ZhenleC/wrapped_acorn_prng/blob/main/LICENSE
////////////////////////////////////////////////////////////////////////////////////////////
// ACORN (Additive Congruential Random Number) generator, a pseudo random number generator
// Inspired from this page: http://acorn.wikramaratna.org/
// Made by Zhenle Cao for the ZerotoASIC course
// Huge shoutout and appreciation to Steven Goldsmith for his invaluable assistance.
// March 2022, to be taped out on MPW5
// 16th order ACORN with modulus = 2^16, and easily scalable to higher modulos.
////////////////////////////////////////////////////////////////////////////////////////////
`default_nettype none
`timescale 1ns/1ps
module acorn_prng (
`ifdef USE_POWER_PINS
inout vccd1, // User area 1 1.8V power
inout vssd1, // User area 1 digital ground
`endif
input clk,
input reset,
input wire load,
input wire [1:0] select,
input wire [11:0] gpio_seed,
input wire [11:0] LA1_seed,
output reg [11:0] out,
output [12:0] io_oeb,
output wire reset_out
);
assign io_oeb = 13'b0;
assign reset_out = reset;
reg [11:0] seed;
reg [11:0] r01;
reg [11:0] r02;
reg [11:0] r03;
reg [11:0] r04;
reg [11:0] r05;
reg [11:0] r06;
reg [11:0] r07;
reg [11:0] r08;
reg [11:0] r09;
reg [11:0] r010;
reg [11:0] r011;
reg [11:0] r012;
reg [11:0] r013;
reg [11:0] r014;
reg [11:0] r015;
reg [11:0] r10;
reg [11:0] r11;
reg [11:0] r12;
reg [11:0] r13;
reg [11:0] r14;
reg [11:0] r15;
reg [11:0] r16;
reg [11:0] r17;
reg [11:0] r18;
reg [11:0] r19;
reg [11:0] r110;
reg [11:0] r111;
reg [11:0] r112;
reg [11:0] r113;
reg [11:0] r114;
reg [11:0] r115;
reg [3:0] counter;
always @(posedge clk) begin
if (reset) begin
//everything to zero
counter <= 0;
r01 <= 0;
r02 <= 0;
r03 <= 0;
r04 <= 0;
r05 <= 0;
r06 <= 0;
r07 <= 0;
r08 <= 0;
r09 <= 0;
r010 <= 0;
r011 <= 0;
r012 <= 0;
r013 <= 0;
r014 <= 0;
r015 <= 0;
r11 <= 0;
r12 <= 0;
r13 <= 0;
r14 <= 0;
r15 <= 0;
r16 <= 0;
r17 <= 0;
r18 <= 0;
r19 <= 0;
r110 <= 0;
r111 <= 0;
r112 <= 0;
r113 <= 0;
r114 <= 0;
r115 <= 0;
out <= 0;
seed <= 0;
end else
if (load) begin
if(select == 2'b00)
seed <= 12'b100000000001;
if(select == 2'b11)
seed <= 12'b111111111111;
if(select == 2'b01)
seed <= gpio_seed;
if(select == 2'b10)
seed <= LA1_seed;
end else begin
counter <= counter + 1;
if(counter == 0)
r11 <= r01+seed;
if(counter == 1)
r12 <= r02+r11;
if(counter == 2)
r13 <= r03+r12;
if(counter == 3)
r14 <= r04+r13;
if(counter == 4)
r15 <= r05+r14;
if(counter == 5)
r16 <= r06+r15;
if(counter == 6)
r17 <= r07+r16;
if(counter == 7)
r18 <= r08+r17;
if(counter == 8)
r19 <= r09+r18;
if(counter == 9)
r110 <= r010+r19;
if(counter == 10)
r111 <= r011+r110;
if(counter == 11)
r112 <= r012+r111;
if(counter == 12)
r113 <= r013+r112;
if(counter == 13)
r114 <= r014+r113;
if(counter == 14)
r115 <= r015+r114;
if(counter == 15)
out <= r115;
r01 <= r11;
r02 <= r12;
r03 <= r13;
r04 <= r14;
r05 <= r15;
r06 <= r16;
r07 <= r17;
r08 <= r18;
r09 <= r19;
r010 <= r110;
r011 <= r111;
r012 <= r112;
r013 <= r113;
r014 <= r114;
r015 <= r115;
end
end
endmodule