picorv32 integration, take 1
This commit is contained in:
parent
9db87cb8ee
commit
fbd3dcef2e
File diff suppressed because it is too large
Load Diff
|
@ -1,30 +0,0 @@
|
|||
# Copyright 2023 (C) Peter McGoron
|
||||
# This file is a part of Upsilon, a free and open source software project.
|
||||
# For license terms, refer to the files in `doc/copying` in the Upsilon
|
||||
# source distribution.
|
||||
# Makefile for tests and hardware verification.
|
||||
|
||||
.PHONY: test clean codegen
|
||||
include ../common.makefile
|
||||
|
||||
all: test codegen
|
||||
test: obj_dir/Vspi_switch
|
||||
|
||||
CODEGEN_FILES= spi_master_ss_preprocessed.v spi_master_preprocessed.v \
|
||||
spi_master_no_write_preprocessed.v \
|
||||
spi_master_no_read_preprocessed.v \
|
||||
spi_master_ss_no_read_preprocessed.v \
|
||||
spi_master_ss_no_write_preprocessed.v spi_switch_preprocessed.v
|
||||
|
||||
codegen: ${CODEGEN_FILES}
|
||||
|
||||
SRC= spi_switch.v spi_switch_sim.cpp
|
||||
obj_dir/Vspi_switch.mk: $(SRC)
|
||||
verilator --cc --exe -Wall \
|
||||
$(SRC)
|
||||
obj_dir/Vspi_switch: obj_dir/Vspi_switch.mk $(SRC)
|
||||
cd obj_dir && make -f Vspi_switch.mk
|
||||
./obj_dir/Vspi_switch
|
||||
|
||||
clean:
|
||||
rm -rf obj_dir/ ${CODEGEN_FILES}
|
|
@ -1,148 +0,0 @@
|
|||
/* Copyright 2023 (C) Peter McGoron
|
||||
* This file is a part of Upsilon, a free and open source software project.
|
||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
||||
* source distribution.
|
||||
*/
|
||||
/* Dynamically adjustable DAC ramping.
|
||||
* Given an increment voltage and a speed setting, increase the voltage
|
||||
* to that voltage in increments over a period of time.
|
||||
* This might not be neccessary for now but I wrote it for possible future
|
||||
* use.
|
||||
*/
|
||||
module ramp #(
|
||||
parameter DAC_DATA_WID = 20,
|
||||
parameter DAC_WID = 24,
|
||||
parameter WAIT_WID = 8,
|
||||
parameter [WAIT_WID-1:0] READ_WAIT = 10
|
||||
) (
|
||||
input clk,
|
||||
input arm,
|
||||
input read_setting,
|
||||
output reg finished,
|
||||
output reg ready,
|
||||
|
||||
input [DAC_WID-1:0] mosi,
|
||||
output [DAC_WID-1:0] miso,
|
||||
output reg arm_transfer,
|
||||
input finished_transfer
|
||||
|
||||
input signed [DAC_DATA_WID-1:0] move_to,
|
||||
input signed [DAC_DATA_WID-1:0] stepsiz,
|
||||
input signed [DAC_DATA_WID-1:0] wait_time,
|
||||
output reg signed [DAC_DATA_WID-1:0] setting
|
||||
);
|
||||
|
||||
localparam WAIT_ON_ARM = 0;
|
||||
localparam WAIT_ON_READ_PART_1 = 1;
|
||||
localparam WAIT_ON_READ_PART_2 = 2;
|
||||
localparam WAIT_ON_WRITE = 3;
|
||||
localparam WAIT_ON_TRANSFER = 4;
|
||||
localparam WAIT_ON_DISARM = 5;
|
||||
localparam WAIT_ON_READ_DISARM = 6;
|
||||
|
||||
reg [3-1:0] state = WAIT_ON_ARM;
|
||||
reg [WAIT_WID-1:0] timer = 0;
|
||||
|
||||
localparam SIGN_POS = 0;
|
||||
localparam SIGN_NEG = 1;
|
||||
reg step_sign = 0;
|
||||
reg last_transfer = 0;
|
||||
|
||||
`define DAC_CMD_WID (DAC_WID - DAC_DATA_WID)
|
||||
localparam [`DAC_CMD_WID-1:0] read_reg = 4'b1001;
|
||||
localparam [`DAC_CMD_WID-1:0] write_reg = 4'b0001;
|
||||
|
||||
task start_transfer();
|
||||
case (step_sign)
|
||||
SIGN_POS: if (setting + stepsiz >= move_to) begin
|
||||
mosi[DAC_DATA_WID-1:0] <= setting;
|
||||
last_transfer <= 1;
|
||||
end else begin
|
||||
mosi[DAC_DATA_WID-1:0] <= setting + stepsiz;
|
||||
end
|
||||
SIGN_NEG: if (setting - stepsiz <= move_to) begin
|
||||
mosi[DAC_DATA_WID-1:0] <= setting;
|
||||
last_transfer <= 1;
|
||||
end else begin
|
||||
mosi[DAC_DATA_WID-1:0] <= setting - stepsiz;
|
||||
end
|
||||
endcase
|
||||
arm_transfer <= 1;
|
||||
endtask
|
||||
|
||||
always @ (posedge clk) begin
|
||||
case (state)
|
||||
WAIT_ON_ARM: if (read_setting) begin
|
||||
mosi <= {read_reg, {(DAC_DATA_WID){1'b0}}};
|
||||
arm_transfer <= 1;
|
||||
state <= WAIT_ON_READ;
|
||||
ready <= 0;
|
||||
end else if (arm) begin
|
||||
ready <= 0;
|
||||
last_transfer <= 0;
|
||||
|
||||
if (realstep != 0) begin
|
||||
state <= WAIT_ON_WRITE;
|
||||
end
|
||||
|
||||
mosi[DAC_WID-1:DAC_DATA_WID] <= write_reg;
|
||||
/* 0 for positve, 1 for negative */
|
||||
step_sign <= move_to > setting;
|
||||
timer <= wait_time;
|
||||
end else begin
|
||||
ready <= 1;
|
||||
end
|
||||
|
||||
/* Why put the wait here? If ramping is necessary then any abrupt
|
||||
* changes in voltages can be disastrous. After each write the
|
||||
* design always waits, even if ramping is stopped or done, so
|
||||
* the next ramp always executes when a safe time has elapsed.
|
||||
*/
|
||||
WAIT_ON_WRITE: if (timer < wait_time) begin
|
||||
timer <= timer + 1;
|
||||
end else if (!arm || last_transfer) begin
|
||||
state <= WAIT_ON_DISARM;
|
||||
finished <= 1;
|
||||
end else if (arm) begin
|
||||
start_transfer();
|
||||
state <= WAIT_ON_TRANSFER;
|
||||
end
|
||||
|
||||
WAIT_ON_TRANSFER: if (finished_transfer) begin
|
||||
setting <= mosi[DAC_DATA_WID-1:0];
|
||||
timer <= 0;
|
||||
state <= WAIT_ON_WRITE;
|
||||
finished <= 1;
|
||||
end
|
||||
|
||||
WAIT_ON_DISARM: if (!arm) begin
|
||||
state <= WAIT_ON_ARM;
|
||||
finished <= 0;
|
||||
end
|
||||
|
||||
WAIT_ON_READ_PART_1: if (finished_transfer) begin
|
||||
state <= WAIT_ON_READ_PART_2;
|
||||
arm_transfer <= 0;
|
||||
mosi <= 0;
|
||||
timer <= 0;
|
||||
end
|
||||
|
||||
WAIT_ON_READ_PART_2: if (timer < read_wait) begin
|
||||
read_wait <= read_wait + 1;
|
||||
end else if (!arm_transfer) begin
|
||||
arm_transfer <= 1;
|
||||
end else if (finished_transfer) begin
|
||||
state <= WAIT_ON_READ_DISARM;
|
||||
finished <= 1;
|
||||
arm_transfer <= 0;
|
||||
setting <= miso[DAC_DATA_WID-1:0];
|
||||
end
|
||||
|
||||
WAIT_ON_READ_DISARM: if (!read_setting) begin
|
||||
state <= WAIT_ON_ARM;
|
||||
finished <= 0;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
endmodule
|
|
@ -1,12 +1,7 @@
|
|||
/* Copyright 2023 (C) Peter McGoron
|
||||
* This file is a part of Upsilon, a free and open source software project.
|
||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
||||
* source distribution.
|
||||
*/
|
||||
/* (c) Peter McGoron 2022 v0.3
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v.2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
/* (c) Peter McGoron 2022-2024 v0.4
|
||||
*
|
||||
* This code is disjunctively dual-licensed under the MPL v2.0, or the
|
||||
* CERN-OHL-W v2.
|
||||
*/
|
||||
|
||||
/* CYCLE_HALF_WAIT should take into account the setup time of the slave
|
||||
|
@ -14,17 +9,9 @@
|
|||
* the input).
|
||||
*/
|
||||
|
||||
module
|
||||
`ifdef SPI_MASTER_NO_READ
|
||||
spi_master_no_read
|
||||
`else
|
||||
`ifdef SPI_MASTER_NO_WRITE
|
||||
spi_master_no_write
|
||||
`else
|
||||
spi_master
|
||||
`endif
|
||||
`endif
|
||||
#(
|
||||
module spi_master #(
|
||||
parameter ENABLE_MISO = 1, // Enable MISO and from_slave port
|
||||
parameter ENABLE_MOSI = 1, // Enable MOSI and to_slave port
|
||||
parameter WID = 24, // Width of bits per transaction.
|
||||
parameter WID_LEN = 5, // Length in bits required to store WID
|
||||
parameter CYCLE_HALF_WAIT = 1, // Half of the wait time of a cycle minus 1.
|
||||
|
@ -32,25 +19,22 @@ spi_master
|
|||
parameter TIMER_LEN = 3, // Length in bits required to store CYCLE_HALF_WAIT
|
||||
parameter POLARITY = 0, // 0 = sck idle low, 1 = sck idle high
|
||||
parameter PHASE = 0 // 0 = rising-read falling-write, 1 = rising-write falling-read.
|
||||
)
|
||||
(
|
||||
) (
|
||||
input clk,
|
||||
input rst_L,
|
||||
`ifndef SPI_MASTER_NO_READ
|
||||
|
||||
output reg [WID-1:0] from_slave,
|
||||
input miso,
|
||||
`endif
|
||||
`ifndef SPI_MASTER_NO_WRITE
|
||||
|
||||
input [WID-1:0] to_slave,
|
||||
output reg mosi,
|
||||
`endif
|
||||
|
||||
output reg sck_wire,
|
||||
output reg finished,
|
||||
output reg ready_to_arm,
|
||||
input arm
|
||||
);
|
||||
|
||||
`ifndef SPI_MASTER_NO_READ
|
||||
/* MISO is almost always an external wire, so buffer it.
|
||||
* This might not be necessary, since the master and slave do not respond
|
||||
* immediately to changes in the wires, but this is just to be safe.
|
||||
|
@ -61,11 +45,10 @@ spi_master
|
|||
reg miso_hot = 0;
|
||||
reg read_miso = 0;
|
||||
|
||||
always @ (posedge clk) begin
|
||||
always @ (posedge clk) if (ENABLE_MISO == 1) begin
|
||||
read_miso <= miso_hot;
|
||||
miso_hot <= miso;
|
||||
end
|
||||
`endif
|
||||
|
||||
parameter WAIT_ON_ARM = 0;
|
||||
parameter ON_CYCLE = 1;
|
||||
|
@ -76,9 +59,7 @@ reg [1:0] state = WAIT_ON_ARM;
|
|||
reg [WID_LEN-1:0] bit_counter = 0;
|
||||
reg [TIMER_LEN-1:0] timer = 0;
|
||||
|
||||
`ifndef SPI_MASTER_NO_WRITE
|
||||
reg [WID-1:0] send_buf = 0;
|
||||
`endif
|
||||
|
||||
reg sck = 0;
|
||||
assign sck_wire = sck;
|
||||
|
@ -89,25 +70,23 @@ task idle_state();
|
|||
end else begin
|
||||
sck <= 1;
|
||||
end
|
||||
`ifndef SPI_MASTER_NO_WRITE
|
||||
mosi <= 0;
|
||||
`endif
|
||||
if (ENABLE_MOSI == 1) mosi <= 0;
|
||||
timer <= 0;
|
||||
bit_counter <= 0;
|
||||
endtask
|
||||
|
||||
task read_data();
|
||||
`ifndef SPI_MASTER_NO_READ
|
||||
from_slave <= from_slave << 1;
|
||||
from_slave[0] <= read_miso;
|
||||
`endif
|
||||
if (ENABLE_MISO == 1) begin
|
||||
from_slave <= from_slave << 1;
|
||||
from_slave[0] <= read_miso;
|
||||
end
|
||||
endtask
|
||||
|
||||
task write_data();
|
||||
`ifndef SPI_MASTER_NO_WRITE
|
||||
mosi <= send_buf[WID-1];
|
||||
send_buf <= send_buf << 1;
|
||||
`endif
|
||||
if (ENABLE_MOSI == 1) begin
|
||||
mosi <= send_buf[WID-1];
|
||||
send_buf <= send_buf << 1;
|
||||
end
|
||||
endtask
|
||||
|
||||
task setup_bits();
|
||||
|
@ -118,15 +97,15 @@ task setup_bits();
|
|||
* For mode 01 and mode 10, the first action is a WRITE.
|
||||
*/
|
||||
if (POLARITY == PHASE) begin
|
||||
`ifndef SPI_MASTER_NO_WRITE
|
||||
mosi <= to_slave[WID-1];
|
||||
send_buf <= to_slave << 1;
|
||||
`endif
|
||||
if (ENABLE_MOSI == 1) begin
|
||||
mosi <= to_slave[WID-1];
|
||||
send_buf <= to_slave << 1;
|
||||
end
|
||||
state <= CYCLE_WAIT;
|
||||
end else begin
|
||||
`ifndef SPI_MASTER_NO_WRITE
|
||||
send_buf <= to_slave;
|
||||
`endif
|
||||
if (ENABLE_MISO == 1) begin
|
||||
send_buf <= to_slave;
|
||||
end
|
||||
state <= ON_CYCLE;
|
||||
end
|
||||
endtask
|
||||
|
@ -149,12 +128,8 @@ always @ (posedge clk) begin
|
|||
finished <= 0;
|
||||
state <= WAIT_ON_ARM;
|
||||
ready_to_arm <= 1;
|
||||
`ifndef SPI_MASTER_NO_READ
|
||||
from_slave <= 0;
|
||||
`endif
|
||||
`ifndef SPI_MASTER_NO_WRITE
|
||||
send_buf <= 0;
|
||||
`endif
|
||||
if (ENABLE_MISO == 1) from_slave <= 0;
|
||||
if (ENABLE_MOSI == 1) send_buf <= 0;
|
||||
end else case (state)
|
||||
WAIT_ON_ARM: begin
|
||||
`ifdef SIMULATION
|
||||
|
@ -230,4 +205,3 @@ always @ (posedge clk) begin
|
|||
end
|
||||
|
||||
endmodule
|
||||
`undefineall
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
/* Copyright 2023 (C) Peter McGoron
|
||||
* This file is a part of Upsilon, a free and open source software project.
|
||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
||||
* source distribution.
|
||||
*/
|
||||
`define SPI_MASTER_NO_READ
|
||||
/* verilator lint_off DECLFILENAME */
|
||||
`include "spi_master.v"
|
|
@ -1,8 +0,0 @@
|
|||
/* Copyright 2023 (C) Peter McGoron
|
||||
* This file is a part of Upsilon, a free and open source software project.
|
||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
||||
* source distribution.
|
||||
*/
|
||||
`define SPI_MASTER_NO_WRITE
|
||||
/* verilator lint_off DECLFILENAME */
|
||||
`include "spi_master.v"
|
|
@ -1,9 +1,126 @@
|
|||
/* Copyright 2023 (C) Peter McGoron
|
||||
* This file is a part of Upsilon, a free and open source software project.
|
||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
||||
* source distribution.
|
||||
/* (c) Peter McGoron 2022 v0.4
|
||||
*
|
||||
* This code is disjunctively dual-licensed under the MPL v2.0, or the
|
||||
* CERN-OHL-W v2.
|
||||
*/
|
||||
`define SPI_MASTER_SS_NAME spi_master_ss
|
||||
`define SPI_MASTER_NAME spi_master
|
||||
/* verilator lint_off DECLFILENAME */
|
||||
`include "spi_master_ss_template.v"
|
||||
|
||||
/* spi master with integrated ability to wait a certain amount of cycles
|
||||
* after activating SS.
|
||||
*/
|
||||
|
||||
module spi_master_ss
|
||||
#(
|
||||
parameter SS_WAIT = 1, /* Amount of cycles to wait for SS
|
||||
to enable */
|
||||
parameter SS_WAIT_TIMER_LEN = 2, /* Amount of bits required to
|
||||
store the SS wait time */
|
||||
|
||||
parameter ENABLE_MISO = 1,
|
||||
parameter ENABLE_MOSI = 1,
|
||||
parameter WID = 24,
|
||||
parameter WID_LEN = 5,
|
||||
parameter CYCLE_HALF_WAIT = 1,
|
||||
parameter TIMER_LEN = 3,
|
||||
|
||||
parameter POLARITY = 0,
|
||||
parameter PHASE = 0
|
||||
) (
|
||||
input clk,
|
||||
input rst_L,
|
||||
|
||||
output [WID-1:0] from_slave,
|
||||
input miso,
|
||||
|
||||
input [WID-1:0] to_slave,
|
||||
output mosi,
|
||||
|
||||
output sck_wire,
|
||||
output finished,
|
||||
output ready_to_arm,
|
||||
output ss_L,
|
||||
input arm
|
||||
);
|
||||
|
||||
reg ss = 0;
|
||||
reg arm_master = 0;
|
||||
assign ss_L = !ss;
|
||||
|
||||
spi_master #(
|
||||
.ENABLE_MISO(ENABLE_MISO),
|
||||
.ENABLE_MOSI(ENABLE_MOSI),
|
||||
.WID(WID),
|
||||
.WID_LEN(WID_LEN),
|
||||
.CYCLE_HALF_WAIT(CYCLE_HALF_WAIT),
|
||||
.TIMER_LEN(TIMER_LEN),
|
||||
.POLARITY(POLARITY),
|
||||
.PHASE(PHASE)
|
||||
) master (
|
||||
.clk(clk),
|
||||
.rst_L(rst_L),
|
||||
|
||||
.from_slave(from_slave),
|
||||
.miso(miso),
|
||||
|
||||
.to_slave(to_slave),
|
||||
.mosi(mosi),
|
||||
|
||||
.sck_wire(sck_wire),
|
||||
.finished(finished),
|
||||
.ready_to_arm(ready_to_arm),
|
||||
.arm(arm_master)
|
||||
);
|
||||
|
||||
localparam WAIT_ON_ARM = 0;
|
||||
localparam WAIT_ON_SS = 1;
|
||||
localparam WAIT_ON_MASTER = 2;
|
||||
localparam WAIT_ON_ARM_DEASSERT = 3;
|
||||
reg [2:0] state = WAIT_ON_ARM;
|
||||
reg [SS_WAIT_TIMER_LEN-1:0] timer = 0;
|
||||
|
||||
task master_arm();
|
||||
arm_master <= 1;
|
||||
state <= WAIT_ON_MASTER;
|
||||
endtask
|
||||
|
||||
always @ (posedge clk) begin
|
||||
if (!rst_L) begin
|
||||
state <= WAIT_ON_ARM;
|
||||
timer <= 0;
|
||||
arm_master <= 0;
|
||||
ss <= 0;
|
||||
end else case (state)
|
||||
WAIT_ON_ARM: begin
|
||||
if (arm) begin
|
||||
timer <= 1;
|
||||
if (SS_WAIT == 0) begin
|
||||
master_arm();
|
||||
end else begin
|
||||
timer <= 1;
|
||||
state <= WAIT_ON_SS;
|
||||
end
|
||||
ss <= 1;
|
||||
end
|
||||
end
|
||||
WAIT_ON_SS: begin
|
||||
if (timer == SS_WAIT) begin
|
||||
master_arm();
|
||||
end else begin
|
||||
timer <= timer + 1;
|
||||
end
|
||||
end
|
||||
WAIT_ON_MASTER: begin
|
||||
if (finished) begin
|
||||
state <= WAIT_ON_ARM_DEASSERT;
|
||||
ss <= 0;
|
||||
end
|
||||
end
|
||||
WAIT_ON_ARM_DEASSERT: begin
|
||||
if (!arm) begin
|
||||
state <= WAIT_ON_ARM;
|
||||
arm_master <= 0;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
/* Copyright 2023 (C) Peter McGoron
|
||||
* This file is a part of Upsilon, a free and open source software project.
|
||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
||||
* source distribution.
|
||||
*/
|
||||
`define SPI_MASTER_SS_NAME spi_master_ss_no_read
|
||||
`define SPI_MASTER_NAME spi_master_no_read
|
||||
`define SPI_MASTER_NO_READ
|
||||
/* verilator lint_off DECLFILENAME */
|
||||
`include "spi_master_ss_template.v"
|
|
@ -1,10 +0,0 @@
|
|||
/* Copyright 2023 (C) Peter McGoron
|
||||
* This file is a part of Upsilon, a free and open source software project.
|
||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
||||
* source distribution.
|
||||
*/
|
||||
`define SPI_MASTER_SS_NAME spi_master_ss_no_write
|
||||
`define SPI_MASTER_NAME spi_master_no_write
|
||||
`define SPI_MASTER_NO_WRITE
|
||||
/* verilator lint_off DECLFILENAME */
|
||||
`include "spi_master_ss_template.v"
|
|
@ -1,128 +0,0 @@
|
|||
/* Copyright 2023 (C) Peter McGoron
|
||||
* This file is a part of Upsilon, a free and open source software project.
|
||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
||||
* source distribution.
|
||||
*/
|
||||
/* (c) Peter McGoron 2022 v0.3
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v.2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
/* spi master with integrated ability to wait a certain amount of cycles
|
||||
* after activating SS.
|
||||
*/
|
||||
|
||||
module `SPI_MASTER_SS_NAME
|
||||
#(
|
||||
parameter WID = 24,
|
||||
parameter WID_LEN = 5,
|
||||
parameter CYCLE_HALF_WAIT = 1,
|
||||
parameter TIMER_LEN = 3,
|
||||
|
||||
parameter SS_WAIT = 1,
|
||||
parameter SS_WAIT_TIMER_LEN = 2,
|
||||
|
||||
parameter POLARITY = 0,
|
||||
parameter PHASE = 0
|
||||
)
|
||||
(
|
||||
input clk,
|
||||
input rst_L,
|
||||
`ifndef SPI_MASTER_NO_READ
|
||||
output [WID-1:0] from_slave,
|
||||
input miso,
|
||||
`endif
|
||||
`ifndef SPI_MASTER_NO_WRITE
|
||||
input [WID-1:0] to_slave,
|
||||
output reg mosi,
|
||||
`endif
|
||||
output sck_wire,
|
||||
output finished,
|
||||
output ready_to_arm,
|
||||
output ss_L,
|
||||
input arm
|
||||
);
|
||||
|
||||
reg ss = 0;
|
||||
reg arm_master = 0;
|
||||
assign ss_L = !ss;
|
||||
|
||||
`SPI_MASTER_NAME #(
|
||||
.WID(WID),
|
||||
.WID_LEN(WID_LEN),
|
||||
.CYCLE_HALF_WAIT(CYCLE_HALF_WAIT),
|
||||
.TIMER_LEN(TIMER_LEN),
|
||||
.POLARITY(POLARITY),
|
||||
.PHASE(PHASE)
|
||||
) master (
|
||||
.clk(clk),
|
||||
.rst_L(rst_L),
|
||||
`ifndef SPI_MASTER_NO_READ
|
||||
.from_slave(from_slave),
|
||||
.miso(miso),
|
||||
`endif
|
||||
`ifndef SPI_MASTER_NO_WRITE
|
||||
.to_slave(to_slave),
|
||||
.mosi(mosi),
|
||||
`endif
|
||||
.sck_wire(sck_wire),
|
||||
.finished(finished),
|
||||
.ready_to_arm(ready_to_arm),
|
||||
.arm(arm_master)
|
||||
);
|
||||
|
||||
localparam WAIT_ON_ARM = 0;
|
||||
localparam WAIT_ON_SS = 1;
|
||||
localparam WAIT_ON_MASTER = 2;
|
||||
localparam WAIT_ON_ARM_DEASSERT = 3;
|
||||
reg [2:0] state = WAIT_ON_ARM;
|
||||
reg [SS_WAIT_TIMER_LEN-1:0] timer = 0;
|
||||
|
||||
task master_arm();
|
||||
arm_master <= 1;
|
||||
state <= WAIT_ON_MASTER;
|
||||
endtask
|
||||
|
||||
always @ (posedge clk) begin
|
||||
if (!rst_L) begin
|
||||
state <= WAIT_ON_ARM;
|
||||
timer <= 0;
|
||||
arm_master <= 0;
|
||||
ss <= 0;
|
||||
end else case (state)
|
||||
WAIT_ON_ARM: begin
|
||||
if (arm) begin
|
||||
timer <= 1;
|
||||
if (SS_WAIT == 0) begin
|
||||
master_arm();
|
||||
end else begin
|
||||
timer <= 1;
|
||||
state <= WAIT_ON_SS;
|
||||
end
|
||||
ss <= 1;
|
||||
end
|
||||
end
|
||||
WAIT_ON_SS: begin
|
||||
if (timer == SS_WAIT) begin
|
||||
master_arm();
|
||||
end else begin
|
||||
timer <= timer + 1;
|
||||
end
|
||||
end
|
||||
WAIT_ON_MASTER: begin
|
||||
if (finished) begin
|
||||
state <= WAIT_ON_ARM_DEASSERT;
|
||||
ss <= 0;
|
||||
end
|
||||
end
|
||||
WAIT_ON_ARM_DEASSERT: begin
|
||||
if (!arm) begin
|
||||
state <= WAIT_ON_ARM;
|
||||
arm_master <= 0;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
endmodule
|
||||
`undefineall
|
|
@ -0,0 +1,106 @@
|
|||
/* (c) Peter McGoron 2022 v0.4
|
||||
*
|
||||
* This code is disjunctively dual-licensed under the MPL v2.0, or the
|
||||
* CERN-OHL-W v2.
|
||||
*/
|
||||
|
||||
module spi_master_ss_wb
|
||||
#(
|
||||
parameter BUS_WID = 32, /* Width of a request on the bus. */
|
||||
|
||||
parameter SS_WAIT = 1,
|
||||
parameter SS_WAIT_TIMER_LEN = 2,
|
||||
|
||||
parameter ENABLE_MISO = 1,
|
||||
parameter ENABLE_MOSI = 1,
|
||||
parameter WID = 24,
|
||||
parameter WID_LEN = 5,
|
||||
parameter CYCLE_HALF_WAIT = 1,
|
||||
parameter TIMER_LEN = 3,
|
||||
|
||||
parameter POLARITY = 0,
|
||||
parameter PHASE = 0
|
||||
) (
|
||||
input clk,
|
||||
input rst_L,
|
||||
|
||||
input miso,
|
||||
output mosi,
|
||||
output sck_wire,
|
||||
output ss_L
|
||||
|
||||
input wb_cyc,
|
||||
input wb_stb,
|
||||
input wb_we,
|
||||
input [(BUS_WID)/4-1:0] wb_sel,
|
||||
input [BUS_WID-1:0] wb_addr,
|
||||
input [BUS_WID-1:0] wb_dat_w,
|
||||
output reg wb_ack,
|
||||
output reg [BUS_WID-1:0] wb_dat_r,
|
||||
);
|
||||
|
||||
/* Address map:
|
||||
|
||||
All words are little endian. Access must be word-aligned and word-level
|
||||
or undefined behavior will occur.
|
||||
|
||||
word 0: ready_to_arm | (finished << 1) (RW)
|
||||
word 1: arm (RO)
|
||||
word 2: from_slave (RO)
|
||||
word 3: to_slave (RW)
|
||||
*/
|
||||
|
||||
wire [WID-1:0] from_slave;
|
||||
reg [WID-1:0] to_slave;
|
||||
wire finished;
|
||||
wire ready_to_arm;
|
||||
reg arm;
|
||||
|
||||
spi_master_ss #(
|
||||
.SS_WAIT(SS_WAIT),
|
||||
.SS_WAIT_TIMER_LEN(SS_WAIT_TIMER_LEN),
|
||||
|
||||
.ENABLE_MISO(ENABLE_MISO),
|
||||
.ENABLE_MOSI(ENABLE_MOSI),
|
||||
.WID(WID),
|
||||
.WID_LEN(WID_LEN),
|
||||
.CYCLE_HALF_WAIT(CYCLE_HALF_WAIT),
|
||||
.TIMER_LEN(TIMER_LEN),
|
||||
|
||||
.POLARITY(POLARITY),
|
||||
.PHASE(PHASE)
|
||||
) spi (
|
||||
.clk(clk),
|
||||
.rst_L(rst_L),
|
||||
|
||||
.from_slave(from_slave),
|
||||
.miso(miso),
|
||||
|
||||
.to_slave(to_slave),
|
||||
.mosi(mosi),
|
||||
|
||||
.sck_wire(sck_wire),
|
||||
.finished(finished),
|
||||
.ready_to_arm(ready_to_arm),
|
||||
.ss_L(ss_L),
|
||||
.arm(arm)
|
||||
);
|
||||
|
||||
always @ (posedge clk) if (wb_cyc && wb_stb && !wb_ack) begin
|
||||
if (!wb_we) case (wb_addr[4-1:0])
|
||||
4'h0: wb_dat_r <= {30'b0, finished, ready_to_arm};
|
||||
4'h4: wb_dat_r <= {31'b0, arm};
|
||||
4'h8: wb_dat_r <= from_slave;
|
||||
4'hC: wb_dat_r <= to_slave;
|
||||
default: wb_dat_r <= 0;
|
||||
endcase else case (wb_addr[4-1:0])
|
||||
4'h4: arm <= wb_dat_w[0];
|
||||
4'hC: to_slave <= wb_dat_w;
|
||||
default: ;
|
||||
end
|
||||
wb_ack <= 1;
|
||||
end else begin
|
||||
wb_ack <= 0;
|
||||
end
|
||||
|
||||
endmodule
|
|
@ -1,171 +0,0 @@
|
|||
/* Copyright 2023 (C) Peter McGoron
|
||||
* This file is a part of Upsilon, a free and open source software project.
|
||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
||||
* source distribution.
|
||||
*/
|
||||
/* (c) Peter McGoron 2022 v0.3
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v.2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
module
|
||||
`ifdef SPI_SLAVE_NO_READ
|
||||
spi_slave_no_read
|
||||
`elsif SPI_SLAVE_NO_WRITE
|
||||
spi_slave_no_write
|
||||
`else
|
||||
spi_slave
|
||||
`endif
|
||||
#(
|
||||
parameter WID = 24, // Width of bits per transaction.
|
||||
parameter WID_LEN = 5, // Length in bits required to store WID
|
||||
parameter POLARITY = 0,
|
||||
parameter PHASE = 0 // 0 = rising-read falling-write, 1 = rising-write falling-read.
|
||||
)
|
||||
(
|
||||
input clk,
|
||||
input rst_L,
|
||||
input sck,
|
||||
input ss_L,
|
||||
`ifndef SPI_SLAVE_NO_READ
|
||||
output reg [WID-1:0] from_master,
|
||||
input mosi,
|
||||
`endif
|
||||
`ifndef SPI_SLAVE_NO_WRITE
|
||||
input [WID-1:0] to_master,
|
||||
output reg miso,
|
||||
`endif
|
||||
output reg finished,
|
||||
input rdy,
|
||||
output reg err
|
||||
);
|
||||
|
||||
`ifndef SPI_SLAVE_NO_READ
|
||||
/* MOSI is almost always an external wire, so buffer it. */
|
||||
reg mosi_hot = 0;
|
||||
reg read_mosi = 0;
|
||||
|
||||
always @ (posedge clk) begin
|
||||
read_mosi <= mosi_hot;
|
||||
mosi_hot <= mosi;
|
||||
end
|
||||
`endif
|
||||
|
||||
wire ss = !ss_L;
|
||||
reg sck_delay = 0;
|
||||
reg [WID_LEN-1:0] bit_counter = 0;
|
||||
reg ss_delay = 0;
|
||||
reg ready_at_start = 0;
|
||||
|
||||
`ifndef SPI_SLAVE_NO_WRITE
|
||||
reg [WID-1:0] send_buf = 0;
|
||||
`endif
|
||||
|
||||
task read_data();
|
||||
`ifndef SPI_SLAVE_NO_READ
|
||||
from_master <= from_master << 1;
|
||||
from_master[0] <= read_mosi;
|
||||
`endif
|
||||
endtask
|
||||
|
||||
task write_data();
|
||||
`ifndef SPI_SLAVE_NO_WRITE
|
||||
send_buf <= send_buf << 1;
|
||||
miso <= send_buf[WID-1];
|
||||
`endif
|
||||
endtask
|
||||
|
||||
task setup_bits();
|
||||
`ifndef SPI_SLAVE_NO_WRITE
|
||||
/* at Mode 00, the transmission starts with
|
||||
* a rising edge, and at mode 11, it starts with a falling
|
||||
* edge. For both modes, these are READs.
|
||||
*
|
||||
* For mode 01 and mode 10, the first action is a WRITE.
|
||||
*/
|
||||
if (POLARITY == PHASE) begin
|
||||
miso <= to_master[WID-1];
|
||||
send_buf <= to_master << 1;
|
||||
end else begin
|
||||
send_buf <= to_master;
|
||||
end
|
||||
`endif
|
||||
endtask
|
||||
|
||||
task check_counter();
|
||||
if (bit_counter == WID[WID_LEN-1:0]) begin
|
||||
err <= ready_at_start;
|
||||
end else begin
|
||||
bit_counter <= bit_counter + 1;
|
||||
end
|
||||
endtask
|
||||
|
||||
always @ (posedge clk) begin
|
||||
if (!rst_L) begin
|
||||
sck_delay <= 0;
|
||||
bit_counter <= 0;
|
||||
ss_delay <= 0;
|
||||
ready_at_start <= 0;
|
||||
`ifndef SPI_SLAVE_NO_READ
|
||||
from_master <= 0;
|
||||
`endif
|
||||
`ifndef SPI_SLAVE_NO_WRITE
|
||||
miso <= 0;
|
||||
send_buf <= 0;
|
||||
`endif
|
||||
finished <= 0;
|
||||
err <= 0;
|
||||
end else begin
|
||||
sck_delay <= sck;
|
||||
ss_delay <= ss;
|
||||
|
||||
case ({ss_delay, ss})
|
||||
2'b01: begin // rising edge of SS
|
||||
bit_counter <= 0;
|
||||
finished <= 0;
|
||||
err <= 0;
|
||||
ready_at_start <= rdy;
|
||||
|
||||
setup_bits();
|
||||
end
|
||||
2'b10: begin // falling edge
|
||||
finished <= ready_at_start;
|
||||
end
|
||||
2'b11: begin
|
||||
case ({sck_delay, sck})
|
||||
2'b01: begin // rising edge
|
||||
if (PHASE == 1) begin
|
||||
write_data();
|
||||
end else begin
|
||||
read_data();
|
||||
end
|
||||
|
||||
if (POLARITY == 0) begin
|
||||
check_counter();
|
||||
end
|
||||
end
|
||||
2'b10: begin // falling edge
|
||||
if (PHASE == 1) begin
|
||||
read_data();
|
||||
end else begin
|
||||
write_data();
|
||||
end
|
||||
|
||||
if (POLARITY == 1) begin
|
||||
check_counter();
|
||||
end
|
||||
end
|
||||
default: ;
|
||||
endcase
|
||||
end
|
||||
2'b00: if (!rdy) begin
|
||||
finished <= 0;
|
||||
err <= 0;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
`undefineall
|
|
@ -1,8 +0,0 @@
|
|||
/* Copyright 2023 (C) Peter McGoron
|
||||
* This file is a part of Upsilon, a free and open source software project.
|
||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
||||
* source distribution.
|
||||
*/
|
||||
`define SPI_SLAVE_NO_READ
|
||||
/* verilator lint_off DECLFILENAME */
|
||||
`include "spi_slave.v"
|
|
@ -1,8 +0,0 @@
|
|||
/* Copyright 2023 (C) Peter McGoron
|
||||
* This file is a part of Upsilon, a free and open source software project.
|
||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
||||
* source distribution.
|
||||
*/
|
||||
`define SPI_SLAVE_NO_WRITE
|
||||
/* verilator lint_off DECLFILENAME */
|
||||
`include "spi_slave.v"
|
|
@ -1,60 +0,0 @@
|
|||
/* Copyright 2023 (C) Peter McGoron
|
||||
* This file is a part of Upsilon, a free and open source software project.
|
||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
||||
* source distribution.
|
||||
*/
|
||||
/* This module is a co-operative crossbar for the wires only. Each end
|
||||
* implements its own SPI master.
|
||||
*
|
||||
* This crossbar is entirely controlled by the kernel.
|
||||
*/
|
||||
module spi_switch #(
|
||||
parameter PORTS = 3
|
||||
) (
|
||||
/* verilator lint_off UNUSEDSIGNAL */
|
||||
input [PORTS-1:0] select,
|
||||
/* verilator lint_on UNUSEDSIGNAL */
|
||||
|
||||
output reg mosi,
|
||||
input miso,
|
||||
output reg sck,
|
||||
output reg ss_L,
|
||||
|
||||
input [PORTS-1:0] mosi_ports,
|
||||
output reg [PORTS-1:0] miso_ports,
|
||||
input [PORTS-1:0] sck_ports,
|
||||
input [PORTS-1:0] ss_L_ports
|
||||
);
|
||||
|
||||
/* Avoid using for loops, they might not synthesize correctly.
|
||||
* Do things the old, dumb way instead.
|
||||
*
|
||||
* TODO: Instead of bit vector, use regular numbers
|
||||
*/
|
||||
|
||||
`define do_select(n) \
|
||||
mosi = mosi_ports[n]; \
|
||||
miso_ports = {{(PORTS-1){1'b0}},miso} << n; \
|
||||
sck = sck_ports[n]; \
|
||||
ss_L = ss_L_ports[n]
|
||||
|
||||
`define check_select(n) \
|
||||
if (select[n]) begin \
|
||||
`do_select(n); \
|
||||
end
|
||||
|
||||
generate if (PORTS == 3) always @(*) begin
|
||||
`check_select(2)
|
||||
else `check_select(1)
|
||||
else begin
|
||||
`do_select(0);
|
||||
end
|
||||
end else always @(*) begin
|
||||
`check_select(1)
|
||||
else begin
|
||||
`do_select(0);
|
||||
end
|
||||
end endgenerate
|
||||
|
||||
endmodule
|
||||
`undefineall
|
|
@ -1,56 +0,0 @@
|
|||
/* Copyright 2023 (C) Peter McGoron
|
||||
* This file is a part of Upsilon, a free and open source software project.
|
||||
* For license terms, refer to the files in `doc/copying` in the Upsilon
|
||||
* source distribution.
|
||||
*/
|
||||
#include "../util.hpp"
|
||||
#include "Vspi_switch.h"
|
||||
Vspi_switch *tb;
|
||||
|
||||
static void set_and_check(unsigned int selected, unsigned int num, unsigned int expected) {
|
||||
tb->miso = 1;
|
||||
tb->mosi_ports = 1 << num;
|
||||
tb->sck_ports = 1 << num;
|
||||
tb->ss_L_ports = 1 << num;
|
||||
tb->eval();
|
||||
|
||||
my_assert(tb->mosi == expected, "%u != %u", tb->mosi, expected);
|
||||
my_assert(tb->sck == expected, "%u != %u", tb->sck, expected);
|
||||
my_assert(tb->ss_L == expected, "%u != %u", tb->ss_L, expected);
|
||||
my_assert(tb->miso_ports == 1 << selected, "%u != %u", tb->miso_ports, 1 << selected);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
Verilated::commandArgs(argc, argv);
|
||||
Verilated::traceEverOn(true);
|
||||
tb = new Vspi_switch();
|
||||
|
||||
printf("Default behavior.\n");
|
||||
tb->select = 0;
|
||||
set_and_check(0, 0, 1);
|
||||
set_and_check(0, 1, 0);
|
||||
set_and_check(0, 2, 0);
|
||||
|
||||
printf("Selecting the first port.\n");
|
||||
tb->select = 1;
|
||||
set_and_check(0, 0, 1);
|
||||
set_and_check(0, 1, 0);
|
||||
set_and_check(0, 2, 0);
|
||||
|
||||
printf("Selecting the second port.\n");
|
||||
tb->select = 1 << 1;
|
||||
set_and_check(1, 0, 0);
|
||||
set_and_check(1, 1, 1);
|
||||
set_and_check(1, 2, 0);
|
||||
|
||||
printf("Selecting the third port.\n");
|
||||
tb->select = 1 << 2;
|
||||
set_and_check(2, 0, 0);
|
||||
set_and_check(2, 1, 0);
|
||||
set_and_check(2, 2, 1);
|
||||
|
||||
tb->final();
|
||||
delete tb;
|
||||
|
||||
return 0;
|
||||
}
|
338
gateware/soc.py
338
gateware/soc.py
|
@ -33,7 +33,7 @@
|
|||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
##########################################################################
|
||||
# Copyright 2023 (C) Peter McGoron
|
||||
# Copyright 2023-2024 (C) Peter McGoron
|
||||
#
|
||||
# This file is a part of Upsilon, a free and open source software project.
|
||||
# For license terms, refer to the files in `doc/copying` in the Upsilon
|
||||
|
@ -58,7 +58,11 @@ from litedram.modules import MT41K128M16
|
|||
from litedram.frontend.dma import LiteDRAMDMAReader
|
||||
from liteeth.phy.mii import LiteEthPHYMII
|
||||
|
||||
import mmio_descr
|
||||
from math import log2, floor
|
||||
|
||||
def minbits(n):
|
||||
""" Return the amount of bits necessary to store n. """
|
||||
return floor(log2(n) + 1)
|
||||
|
||||
"""
|
||||
Keep this diagram up to date! This is the wiring diagram from the ADC to
|
||||
|
@ -101,98 +105,192 @@ io = [
|
|||
("test_clock", 0, Pins("P18"), IOStandard("LVCMOS33"))
|
||||
]
|
||||
|
||||
# TODO: Assign widths to ADCs here using parameters
|
||||
class PreemptiveInterface(Module, AutoCSR):
|
||||
""" A preemptive interface is a manually controlled Wishbone interface
|
||||
that stands between multiple masters (potentially interconnects) and a
|
||||
single slave. A master controls which master (or interconnect) has access
|
||||
to the slave. This is to avoid bus contention by having multiple buses. """
|
||||
|
||||
class Base(Module, AutoCSR):
|
||||
""" The subclass AutoCSR will automatically make CSRs related
|
||||
to this class when those CSRs are attributes (i.e. accessed by
|
||||
`self.csr_name`) of instances of this class. (CSRs are MMIO,
|
||||
they are NOT RISC-V CSRs!)
|
||||
|
||||
Since there are a lot of input and output wires, the CSRs are
|
||||
assigned using `setattr()`.
|
||||
|
||||
CSRs are for input wires (`CSRStorage`) or output wires
|
||||
(`CSRStatus`). The first argument to the CSR constructor is
|
||||
the amount of bits the CSR takes. The `name` keyword argument
|
||||
is required since the constructor needs the name of the attribute.
|
||||
The `description` keyword is used for documentation.
|
||||
|
||||
In LiteX, modules in separate Verilog files are instantiated as
|
||||
self.specials += Instance(
|
||||
"module_name",
|
||||
PARAMETER_NAME=value,
|
||||
i_input = input_port,
|
||||
o_output = output_port,
|
||||
...
|
||||
)
|
||||
|
||||
Since the "base" module has a bunch of repeated input and output
|
||||
pins that have to be connected to CSRs, the LiteX wrapper uses
|
||||
keyword arguments to pass all the arguments.
|
||||
"""
|
||||
|
||||
def _make_csr(self, reg, num=None):
|
||||
""" Add a CSR for a pin `f"{name}_{num}".`
|
||||
|
||||
:param name: Name of the MMIO register without prefixes or numerical
|
||||
suffix.
|
||||
:param num: Numerical suffix of this MMIO register. This is the only
|
||||
parameter that should change when adding multiple CSRs of the same
|
||||
name.
|
||||
def __init__(self, masters_len, slave):
|
||||
"""
|
||||
:param masters_len: The amount of buses accessing this slave. This number
|
||||
must be greater than one.
|
||||
:param slave: The slave device. This object must have an Interface object
|
||||
accessable as ``bus``.
|
||||
"""
|
||||
|
||||
name = reg.name
|
||||
if num is not None:
|
||||
name = f"{name}_{num}"
|
||||
assert masters_len > 1
|
||||
self.buses = []
|
||||
self.master_select = CSRStorage(masters_len, name='master_select', description='RW bitstring of which master interconnect to connect to')
|
||||
self.slave = slave
|
||||
|
||||
if reg.rwperm == "read-only":
|
||||
csrclass = CSRStatus
|
||||
else:
|
||||
csrclass = CSRStorage
|
||||
for i in range(masters_len):
|
||||
# Add the slave interface each master interconnect sees.
|
||||
self.buses.append(Interface(data_width=32, address_width=32, addressing="byte")
|
||||
self.comb += [
|
||||
self.buses[i].cti.eq(0),
|
||||
self.buses[i].bte.eq(0),
|
||||
]
|
||||
|
||||
csr = csrclass(reg.blen, name=name, description=None)
|
||||
setattr(self, name, csr)
|
||||
"""
|
||||
Construct a combinatorial case statement. In verilog, the if
|
||||
statment would look like
|
||||
|
||||
if csrclass is CSRStorage:
|
||||
self.kwargs[f'i_{name}'] = csr.storage
|
||||
elif csrclass is CSRStatus:
|
||||
self.kwargs[f'o_{name}'] = csr.status
|
||||
else:
|
||||
raise Exception(f"Unknown class {csrclass}")
|
||||
always @ (*) case (master_select)
|
||||
1: begin
|
||||
// Bus assignments...
|
||||
end
|
||||
2: begin
|
||||
// Bus assignments...
|
||||
end
|
||||
// more cases:
|
||||
default:
|
||||
// assign everything to master 0
|
||||
end
|
||||
|
||||
def __init__(self, clk, sdram, platform):
|
||||
self.kwargs = {}
|
||||
The If statement in Migen (Python HDL) is an object with a method
|
||||
called "ElseIf" and "Else", that return objects with the specified
|
||||
case attached. Instead of directly writing an If statement into
|
||||
the combinatorial block, the If statement is constructed in a
|
||||
for loop.
|
||||
|
||||
for reg in mmio_descr.registers:
|
||||
if reg.num > 1:
|
||||
for i in range(0,reg.num):
|
||||
self._make_csr(reg,i)
|
||||
else:
|
||||
self._make_csr(reg)
|
||||
The "assign_for_case" function constructs the body of the If
|
||||
statement. It assigns all output ports to avoid latches.
|
||||
"""
|
||||
|
||||
self.kwargs["i_clk"] = clk
|
||||
self.kwargs["i_rst_L"] = ~platform.request("module_reset")
|
||||
self.kwargs["i_dac_miso"] = platform.request("dac_miso")
|
||||
self.kwargs["o_dac_mosi"] = platform.request("dac_mosi")
|
||||
self.kwargs["o_dac_sck"] = platform.request("dac_sck")
|
||||
self.kwargs["o_dac_ss_L"] = platform.request("dac_ss_L")
|
||||
self.kwargs["o_adc_conv"] = platform.request("adc_conv")
|
||||
self.kwargs["i_adc_sdo"] = platform.request("adc_sdo")
|
||||
self.kwargs["o_adc_sck"] = platform.request("adc_sck")
|
||||
self.kwargs["o_set_low"] = platform.request("differntial_output_low")
|
||||
def assign_for_case(i):
|
||||
asn = [
|
||||
self.slave.bus.cyc.eq(self.buses[i].cyc),
|
||||
self.slave.bus.stb.eq(self.buses[i].stb),
|
||||
self.slave.bus.we.eq(self.buses[i].we),
|
||||
self.slave.bus.sel.eq(self.buses[i].sel),
|
||||
self.slave.bus.addr.eq(self.buses[i].addr),
|
||||
self.slave.bus.dat_w.eq(self.buses[i].dat_w),
|
||||
self.slave.bus.ack.eq(self.buses[i].ack),
|
||||
self.slave.bus.dat_r.eq(self.buses[i].dat_r),
|
||||
]
|
||||
|
||||
self.specials += Instance("base", **self.kwargs)
|
||||
for j in range(masters_len):
|
||||
if j == i:
|
||||
continue
|
||||
asn += [
|
||||
self.buses[i].ack.eq(0),
|
||||
self.buses[i].ack.eq(0),
|
||||
]
|
||||
|
||||
return asn
|
||||
|
||||
cases = {"default": assign_for_case(0)}
|
||||
for i in range(1, masters_len):
|
||||
cases[i] = assign_for_case(i)
|
||||
|
||||
self.comb += Case(self.master_select, cases)
|
||||
|
||||
class SPIMaster(Module):
|
||||
def __init__(self, rst, miso, mosi, sck, ss,
|
||||
polarity = 0,
|
||||
phase = 0,
|
||||
ss_wait = 1,
|
||||
enable_miso = 1,
|
||||
enable_mosi = 1,
|
||||
spi_wid = 24,
|
||||
spi_cycle_half_wait = 1,
|
||||
):
|
||||
|
||||
self.bus = Interface(data_width = 32, address_width=32, addressing="word")
|
||||
self.comb += [
|
||||
self.bus.cti.eq(0),
|
||||
self.bus.bte.eq(0),
|
||||
]
|
||||
|
||||
self.specials += Instance("spi_master_ss_wb",
|
||||
SS_WAIT = ss_wait,
|
||||
SS_WAIT_TIMER_LEN = minwid(ss_wait),
|
||||
CYCLE_HALF_WAIT = spi_cycle_half_wait,
|
||||
TIMER_LEN = minwid(spi_cycle_half_wait),
|
||||
WID = spi_wid,
|
||||
WID_LEN = minwid(spi_wid),
|
||||
ENABLE_MISO = enable_miso,
|
||||
ENABLE_MOSI = enable_mosi,
|
||||
POLARITY = polarity,
|
||||
PHASE = phase,
|
||||
|
||||
i_clk = ClockSignal(),
|
||||
i_rst_L = rst,
|
||||
i_miso = miso,
|
||||
o_mosi = mosi,
|
||||
o_sck_wire = sck,
|
||||
o_ss_L = ss,
|
||||
|
||||
i_wb_cyc = self.bus.cyc,
|
||||
i_wb_stb = self.bus.stb,
|
||||
i_wb_we = self.bus.we,
|
||||
i_wb_sel = self.bus.sel,
|
||||
i_wb_addr = self.bus.adr,
|
||||
i_wb_dat_w = self.bus.dat_w,
|
||||
o_wb_ack = self.bus.ack,
|
||||
o_wb_dat_r = self.bus.dat_r,
|
||||
)
|
||||
|
||||
# TODO: Generalize CSR stuff
|
||||
class ControlLoopParameters(Module, AutoCSR):
|
||||
def __init__(self):
|
||||
self.cl_I = CSRStorage(32, description='Integral parameter')
|
||||
self.cl_P = CSRStorage(32, description='Proportional parameter')
|
||||
self.deltaT = CSRStorage(32, description='Wait parameter')
|
||||
self.setpt = CSRStorage(32, description='Setpoint')
|
||||
self.zset = CSRStatus(32, description='Set Z position')
|
||||
self.zpos = CSRStatus(32, description='Measured Z position')
|
||||
|
||||
self.bus = Interface(data_width = 32, address_width = 32, addressing="word")
|
||||
self.region = SoCRegion(size=minbits(0x14), cached=False)
|
||||
self.comb += [
|
||||
self.bus.cti.eq(0),
|
||||
self.bus.bte.eq(0),
|
||||
]
|
||||
self.sync += [
|
||||
If(self.bus.cyc && self.bus.stb && !self.bus.ack,
|
||||
Case(self.bus.adr[0:4], {
|
||||
0x0: self.bus.dat_r.eq(self.cl_I),
|
||||
0x4: self.bus.dat_r.eq(self.cl_P),
|
||||
0x8: self.bus.dat_r.eq(self.deltaT),
|
||||
0xC: self.bus.dat_r.eq(self.setpt),
|
||||
0x10: If(self.bus.we,
|
||||
self.bus.zset.eq(self.bus.dat_w)
|
||||
).Else(
|
||||
self.bus.dat_r.eq(self.bus.zset)
|
||||
),
|
||||
0x14: If(self.bus.we,
|
||||
self.bus.zpos.eq(self.bus.dat_w),
|
||||
).Else(
|
||||
self.bus.dat_r.eq(self.bus.zpos)
|
||||
),
|
||||
}),
|
||||
self.bus.ack.eq(1),
|
||||
).Else(
|
||||
self.bus.ack.eq(0),
|
||||
)
|
||||
]
|
||||
|
||||
class BRAM(Module):
|
||||
def __init__(self, clk):
|
||||
""" A BRAM (block ram) is a memory store that is completely separate from
|
||||
the system RAM. They are much smaller.
|
||||
"""
|
||||
def __init__(self, addr_mask):
|
||||
"""
|
||||
:param addr_mask: Mask which defines the amount of bytes accessable
|
||||
by the BRAM.
|
||||
"""
|
||||
self.bus = Interface(data_width=32, address_width=32, addressing="byte")
|
||||
self.region = SoCRegion(size=addr_mask+1, cached=False)
|
||||
|
||||
self.comb += [
|
||||
self.bus.cti.eq(0),
|
||||
self.bus.bte.eq(0),
|
||||
]
|
||||
self.specials += Instance("bram",
|
||||
i_clk = clk,
|
||||
ADDR_MASK = addr_mask,
|
||||
i_clk = ClockSignal(),
|
||||
i_wb_cyc = self.bus.cyc,
|
||||
i_wb_stb = self.bus.stb,
|
||||
i_wb_we = self.bus.we,
|
||||
|
@ -203,6 +301,64 @@ class BRAM(Module):
|
|||
o_wb_dat_r = self.bus.dat_r,
|
||||
)
|
||||
|
||||
class PicoRV32(Module, AutoCSR):
|
||||
def __init__(self, bramwid=0x1000):
|
||||
self.submodules.params = ControlLoopParameters()
|
||||
self.submodules.bram = BRAM(bramwid-1)
|
||||
self.submodules.bram_iface = PreemptiveInterface(2, self.submodules.bram)
|
||||
|
||||
self.masterbus = Interface(data_width=32, address_width=32, addressing="byte")
|
||||
|
||||
self.resetpin = CSRStorage(1, name="picorv32_reset", description="PicoRV32 reset")
|
||||
self.trap = CSRStatus(1, name="picorv32_trap", description="Trap bit")
|
||||
|
||||
ic = SoCBusHandler(
|
||||
standard="wishbone",
|
||||
data_width=32,
|
||||
address_width=32,
|
||||
timeout=1e6,
|
||||
bursting=False,
|
||||
interconnect="shared",
|
||||
interconnect_register=True,
|
||||
reserved_regions={
|
||||
"picorv32_null_region": SoCRegion(origin=0,size=0xFFFF, mode="ro", cached=False, decode=False)
|
||||
},
|
||||
)
|
||||
|
||||
ic.add_slave("picorv32_params", self.submodules.params, self.submodules.params.region)
|
||||
ic.add_slave("picorv32_bram", self.submodules.bram_iface, self.submodules.bram.region)
|
||||
ic.add_master("picorv32_master", self.masterbus)
|
||||
|
||||
self.specials += Instance("picorv32_wb",
|
||||
o_trap = self.trap.status,
|
||||
|
||||
i_wb_rst_i = self.resetpin.storage,
|
||||
i_wb_clk_i = ClockSignal(),
|
||||
o_wbm_adr_o = self.masterbus.adr,
|
||||
o_wbm_dat_o = self.masterbus.dat_r,
|
||||
i_wbm_dat_i = self.masterbus.dat_w,
|
||||
o_wbm_we_o = self.masterbus.we,
|
||||
o_wbm_sel_o = self.masterbus.sel,
|
||||
o_wbm_stb_o = self.masterbus.stb,
|
||||
i_wbm_ack_i = self.masterbus.ack,
|
||||
o_wbm_cyc_o = self.masterbus.cyc,
|
||||
|
||||
o_pcpi_valid = Signal(),
|
||||
o_pcpi_insn = Signal(32),
|
||||
o_pcpi_rs1 = Signal(32),
|
||||
o_pcpi_rs2 = Signal(32),
|
||||
i_pcpi_wr = 0,
|
||||
i_pcpi_wait = 0,
|
||||
i_pcpi_rd = 0,
|
||||
i_pcpi_ready = 0,
|
||||
|
||||
i_irq = 0,
|
||||
o_eoi = Signal(32),
|
||||
|
||||
o_trace_valid = Signal(),
|
||||
o_trace_data = Signal(36),
|
||||
)
|
||||
|
||||
# Clock and Reset Generator
|
||||
# I don't know how this works, I only know that it does.
|
||||
class _CRG(Module):
|
||||
|
@ -240,10 +396,10 @@ class UpsilonSoC(SoCCore):
|
|||
def add_ip(self, ip_str, ip_name):
|
||||
for seg_num, ip_byte in enumerate(ip_str.split('.'),start=1):
|
||||
self.add_constant(f"{ip_name}{seg_num}", int(ip_byte))
|
||||
def add_bram(self, region_name):
|
||||
self.bus.add_region(region_name, SoCRegion(size=0x2000, cached=False))
|
||||
# TODO: special name
|
||||
self.submodules.bram0 = BRAM(ClockSignal())
|
||||
|
||||
def add_picorv32(self):
|
||||
self.submodules.picorv32 = pr = PicoRV32()
|
||||
self.bus.add_region("picorv32_master_bram", pr.submodules.bram.region)
|
||||
|
||||
def __init__(self,
|
||||
variant="a7-100",
|
||||
|
@ -267,23 +423,11 @@ class UpsilonSoC(SoCCore):
|
|||
Since Yosys doesn't support modern Verilog, only put preprocessed
|
||||
(if applicable) files here.
|
||||
"""
|
||||
platform.add_source("rtl/spi/spi_switch_preprocessed.v")
|
||||
platform.add_source("rtl/spi/spi_master_preprocessed.v")
|
||||
platform.add_source("rtl/spi/spi_master_no_write_preprocessed.v")
|
||||
platform.add_source("rtl/spi/spi_master_no_read_preprocessed.v")
|
||||
platform.add_source("rtl/spi/spi_master_ss_preprocessed.v")
|
||||
platform.add_source("rtl/spi/spi_master_ss_no_write_preprocessed.v")
|
||||
platform.add_source("rtl/spi/spi_master_ss_no_read_preprocessed.v")
|
||||
platform.add_source("rtl/control_loop/sign_extend.v")
|
||||
platform.add_source("rtl/control_loop/intsat.v")
|
||||
platform.add_source("rtl/control_loop/boothmul_preprocessed.v")
|
||||
platform.add_source("rtl/control_loop/control_loop_math.v")
|
||||
platform.add_source("rtl/control_loop/control_loop.v")
|
||||
# platform.add_source("rtl/waveform/bram_interface_preprocessed.v")
|
||||
# platform.add_source("rtl/waveform/waveform_preprocessed.v")
|
||||
# when SoC cannot find a source file, it will fail with a confusing error message
|
||||
platform.add_source("rtl/picorv32/picorv32.v")
|
||||
platform.add_source("rtl/spi/spi_master.v")
|
||||
platform.add_source("rtl/spi/spi_master_ss.v")
|
||||
platform.add_source("rtl/spi/spi_master_ss_wb.v")
|
||||
platform.add_source("rtl/bram/bram.v")
|
||||
platform.add_source("rtl/base/base.v")
|
||||
|
||||
# SoCCore does not have sane defaults (no integrated rom)
|
||||
SoCCore.__init__(self,
|
||||
|
@ -330,8 +474,6 @@ class UpsilonSoC(SoCCore):
|
|||
self.add_ip(remote_ip, "REMOTEIP")
|
||||
self.add_constant("TFTP_SERVER_PORT", tftp_port)
|
||||
|
||||
self.add_bram("bram0")
|
||||
|
||||
# Add pins
|
||||
platform.add_extension(io)
|
||||
self.submodules.base = Base(ClockSignal(), self.sdram, platform)
|
||||
|
|
Loading…
Reference in New Issue