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
|
/* (c) Peter McGoron 2022-2024 v0.4
|
||||||
* 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
|
* This code is disjunctively dual-licensed under the MPL v2.0, or the
|
||||||
* source distribution.
|
* CERN-OHL-W v2.
|
||||||
*/
|
|
||||||
/* (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/.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* CYCLE_HALF_WAIT should take into account the setup time of the slave
|
/* CYCLE_HALF_WAIT should take into account the setup time of the slave
|
||||||
|
@ -14,17 +9,9 @@
|
||||||
* the input).
|
* the input).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
module
|
module spi_master #(
|
||||||
`ifdef SPI_MASTER_NO_READ
|
parameter ENABLE_MISO = 1, // Enable MISO and from_slave port
|
||||||
spi_master_no_read
|
parameter ENABLE_MOSI = 1, // Enable MOSI and to_slave port
|
||||||
`else
|
|
||||||
`ifdef SPI_MASTER_NO_WRITE
|
|
||||||
spi_master_no_write
|
|
||||||
`else
|
|
||||||
spi_master
|
|
||||||
`endif
|
|
||||||
`endif
|
|
||||||
#(
|
|
||||||
parameter WID = 24, // Width of bits per transaction.
|
parameter WID = 24, // Width of bits per transaction.
|
||||||
parameter WID_LEN = 5, // Length in bits required to store WID
|
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.
|
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 TIMER_LEN = 3, // Length in bits required to store CYCLE_HALF_WAIT
|
||||||
parameter POLARITY = 0, // 0 = sck idle low, 1 = sck idle high
|
parameter POLARITY = 0, // 0 = sck idle low, 1 = sck idle high
|
||||||
parameter PHASE = 0 // 0 = rising-read falling-write, 1 = rising-write falling-read.
|
parameter PHASE = 0 // 0 = rising-read falling-write, 1 = rising-write falling-read.
|
||||||
)
|
) (
|
||||||
(
|
|
||||||
input clk,
|
input clk,
|
||||||
input rst_L,
|
input rst_L,
|
||||||
`ifndef SPI_MASTER_NO_READ
|
|
||||||
output reg [WID-1:0] from_slave,
|
output reg [WID-1:0] from_slave,
|
||||||
input miso,
|
input miso,
|
||||||
`endif
|
|
||||||
`ifndef SPI_MASTER_NO_WRITE
|
|
||||||
input [WID-1:0] to_slave,
|
input [WID-1:0] to_slave,
|
||||||
output reg mosi,
|
output reg mosi,
|
||||||
`endif
|
|
||||||
output reg sck_wire,
|
output reg sck_wire,
|
||||||
output reg finished,
|
output reg finished,
|
||||||
output reg ready_to_arm,
|
output reg ready_to_arm,
|
||||||
input arm
|
input arm
|
||||||
);
|
);
|
||||||
|
|
||||||
`ifndef SPI_MASTER_NO_READ
|
|
||||||
/* MISO is almost always an external wire, so buffer it.
|
/* MISO is almost always an external wire, so buffer it.
|
||||||
* This might not be necessary, since the master and slave do not respond
|
* 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.
|
* immediately to changes in the wires, but this is just to be safe.
|
||||||
|
@ -61,11 +45,10 @@ spi_master
|
||||||
reg miso_hot = 0;
|
reg miso_hot = 0;
|
||||||
reg read_miso = 0;
|
reg read_miso = 0;
|
||||||
|
|
||||||
always @ (posedge clk) begin
|
always @ (posedge clk) if (ENABLE_MISO == 1) begin
|
||||||
read_miso <= miso_hot;
|
read_miso <= miso_hot;
|
||||||
miso_hot <= miso;
|
miso_hot <= miso;
|
||||||
end
|
end
|
||||||
`endif
|
|
||||||
|
|
||||||
parameter WAIT_ON_ARM = 0;
|
parameter WAIT_ON_ARM = 0;
|
||||||
parameter ON_CYCLE = 1;
|
parameter ON_CYCLE = 1;
|
||||||
|
@ -76,9 +59,7 @@ reg [1:0] state = WAIT_ON_ARM;
|
||||||
reg [WID_LEN-1:0] bit_counter = 0;
|
reg [WID_LEN-1:0] bit_counter = 0;
|
||||||
reg [TIMER_LEN-1:0] timer = 0;
|
reg [TIMER_LEN-1:0] timer = 0;
|
||||||
|
|
||||||
`ifndef SPI_MASTER_NO_WRITE
|
|
||||||
reg [WID-1:0] send_buf = 0;
|
reg [WID-1:0] send_buf = 0;
|
||||||
`endif
|
|
||||||
|
|
||||||
reg sck = 0;
|
reg sck = 0;
|
||||||
assign sck_wire = sck;
|
assign sck_wire = sck;
|
||||||
|
@ -89,25 +70,23 @@ task idle_state();
|
||||||
end else begin
|
end else begin
|
||||||
sck <= 1;
|
sck <= 1;
|
||||||
end
|
end
|
||||||
`ifndef SPI_MASTER_NO_WRITE
|
if (ENABLE_MOSI == 1) mosi <= 0;
|
||||||
mosi <= 0;
|
|
||||||
`endif
|
|
||||||
timer <= 0;
|
timer <= 0;
|
||||||
bit_counter <= 0;
|
bit_counter <= 0;
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
task read_data();
|
task read_data();
|
||||||
`ifndef SPI_MASTER_NO_READ
|
if (ENABLE_MISO == 1) begin
|
||||||
from_slave <= from_slave << 1;
|
from_slave <= from_slave << 1;
|
||||||
from_slave[0] <= read_miso;
|
from_slave[0] <= read_miso;
|
||||||
`endif
|
end
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
task write_data();
|
task write_data();
|
||||||
`ifndef SPI_MASTER_NO_WRITE
|
if (ENABLE_MOSI == 1) begin
|
||||||
mosi <= send_buf[WID-1];
|
mosi <= send_buf[WID-1];
|
||||||
send_buf <= send_buf << 1;
|
send_buf <= send_buf << 1;
|
||||||
`endif
|
end
|
||||||
endtask
|
endtask
|
||||||
|
|
||||||
task setup_bits();
|
task setup_bits();
|
||||||
|
@ -118,15 +97,15 @@ task setup_bits();
|
||||||
* For mode 01 and mode 10, the first action is a WRITE.
|
* For mode 01 and mode 10, the first action is a WRITE.
|
||||||
*/
|
*/
|
||||||
if (POLARITY == PHASE) begin
|
if (POLARITY == PHASE) begin
|
||||||
`ifndef SPI_MASTER_NO_WRITE
|
if (ENABLE_MOSI == 1) begin
|
||||||
mosi <= to_slave[WID-1];
|
mosi <= to_slave[WID-1];
|
||||||
send_buf <= to_slave << 1;
|
send_buf <= to_slave << 1;
|
||||||
`endif
|
end
|
||||||
state <= CYCLE_WAIT;
|
state <= CYCLE_WAIT;
|
||||||
end else begin
|
end else begin
|
||||||
`ifndef SPI_MASTER_NO_WRITE
|
if (ENABLE_MISO == 1) begin
|
||||||
send_buf <= to_slave;
|
send_buf <= to_slave;
|
||||||
`endif
|
end
|
||||||
state <= ON_CYCLE;
|
state <= ON_CYCLE;
|
||||||
end
|
end
|
||||||
endtask
|
endtask
|
||||||
|
@ -149,12 +128,8 @@ always @ (posedge clk) begin
|
||||||
finished <= 0;
|
finished <= 0;
|
||||||
state <= WAIT_ON_ARM;
|
state <= WAIT_ON_ARM;
|
||||||
ready_to_arm <= 1;
|
ready_to_arm <= 1;
|
||||||
`ifndef SPI_MASTER_NO_READ
|
if (ENABLE_MISO == 1) from_slave <= 0;
|
||||||
from_slave <= 0;
|
if (ENABLE_MOSI == 1) send_buf <= 0;
|
||||||
`endif
|
|
||||||
`ifndef SPI_MASTER_NO_WRITE
|
|
||||||
send_buf <= 0;
|
|
||||||
`endif
|
|
||||||
end else case (state)
|
end else case (state)
|
||||||
WAIT_ON_ARM: begin
|
WAIT_ON_ARM: begin
|
||||||
`ifdef SIMULATION
|
`ifdef SIMULATION
|
||||||
|
@ -230,4 +205,3 @@ always @ (posedge clk) begin
|
||||||
end
|
end
|
||||||
|
|
||||||
endmodule
|
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
|
/* (c) Peter McGoron 2022 v0.4
|
||||||
* 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
|
* This code is disjunctively dual-licensed under the MPL v2.0, or the
|
||||||
* source distribution.
|
* CERN-OHL-W v2.
|
||||||
*/
|
*/
|
||||||
`define SPI_MASTER_SS_NAME spi_master_ss
|
|
||||||
`define SPI_MASTER_NAME spi_master
|
/* spi master with integrated ability to wait a certain amount of cycles
|
||||||
/* verilator lint_off DECLFILENAME */
|
* after activating SS.
|
||||||
`include "spi_master_ss_template.v"
|
*/
|
||||||
|
|
||||||
|
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
|
# 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.
|
# 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.
|
# 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
|
# 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 litedram.frontend.dma import LiteDRAMDMAReader
|
||||||
from liteeth.phy.mii import LiteEthPHYMII
|
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
|
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"))
|
("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):
|
def __init__(self, masters_len, slave):
|
||||||
""" The subclass AutoCSR will automatically make CSRs related
|
"""
|
||||||
to this class when those CSRs are attributes (i.e. accessed by
|
:param masters_len: The amount of buses accessing this slave. This number
|
||||||
`self.csr_name`) of instances of this class. (CSRs are MMIO,
|
must be greater than one.
|
||||||
they are NOT RISC-V CSRs!)
|
:param slave: The slave device. This object must have an Interface object
|
||||||
|
accessable as ``bus``.
|
||||||
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.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name = reg.name
|
assert masters_len > 1
|
||||||
if num is not None:
|
self.buses = []
|
||||||
name = f"{name}_{num}"
|
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":
|
for i in range(masters_len):
|
||||||
csrclass = CSRStatus
|
# Add the slave interface each master interconnect sees.
|
||||||
else:
|
self.buses.append(Interface(data_width=32, address_width=32, addressing="byte")
|
||||||
csrclass = CSRStorage
|
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:
|
always @ (*) case (master_select)
|
||||||
self.kwargs[f'i_{name}'] = csr.storage
|
1: begin
|
||||||
elif csrclass is CSRStatus:
|
// Bus assignments...
|
||||||
self.kwargs[f'o_{name}'] = csr.status
|
end
|
||||||
else:
|
2: begin
|
||||||
raise Exception(f"Unknown class {csrclass}")
|
// Bus assignments...
|
||||||
|
end
|
||||||
|
// more cases:
|
||||||
|
default:
|
||||||
|
// assign everything to master 0
|
||||||
|
end
|
||||||
|
|
||||||
def __init__(self, clk, sdram, platform):
|
The If statement in Migen (Python HDL) is an object with a method
|
||||||
self.kwargs = {}
|
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:
|
The "assign_for_case" function constructs the body of the If
|
||||||
if reg.num > 1:
|
statement. It assigns all output ports to avoid latches.
|
||||||
for i in range(0,reg.num):
|
"""
|
||||||
self._make_csr(reg,i)
|
|
||||||
else:
|
|
||||||
self._make_csr(reg)
|
|
||||||
|
|
||||||
self.kwargs["i_clk"] = clk
|
def assign_for_case(i):
|
||||||
self.kwargs["i_rst_L"] = ~platform.request("module_reset")
|
asn = [
|
||||||
self.kwargs["i_dac_miso"] = platform.request("dac_miso")
|
self.slave.bus.cyc.eq(self.buses[i].cyc),
|
||||||
self.kwargs["o_dac_mosi"] = platform.request("dac_mosi")
|
self.slave.bus.stb.eq(self.buses[i].stb),
|
||||||
self.kwargs["o_dac_sck"] = platform.request("dac_sck")
|
self.slave.bus.we.eq(self.buses[i].we),
|
||||||
self.kwargs["o_dac_ss_L"] = platform.request("dac_ss_L")
|
self.slave.bus.sel.eq(self.buses[i].sel),
|
||||||
self.kwargs["o_adc_conv"] = platform.request("adc_conv")
|
self.slave.bus.addr.eq(self.buses[i].addr),
|
||||||
self.kwargs["i_adc_sdo"] = platform.request("adc_sdo")
|
self.slave.bus.dat_w.eq(self.buses[i].dat_w),
|
||||||
self.kwargs["o_adc_sck"] = platform.request("adc_sck")
|
self.slave.bus.ack.eq(self.buses[i].ack),
|
||||||
self.kwargs["o_set_low"] = platform.request("differntial_output_low")
|
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):
|
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.bus = Interface(data_width=32, address_width=32, addressing="byte")
|
||||||
|
self.region = SoCRegion(size=addr_mask+1, cached=False)
|
||||||
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
self.bus.cti.eq(0),
|
self.bus.cti.eq(0),
|
||||||
self.bus.bte.eq(0),
|
self.bus.bte.eq(0),
|
||||||
]
|
]
|
||||||
self.specials += Instance("bram",
|
self.specials += Instance("bram",
|
||||||
i_clk = clk,
|
ADDR_MASK = addr_mask,
|
||||||
|
i_clk = ClockSignal(),
|
||||||
i_wb_cyc = self.bus.cyc,
|
i_wb_cyc = self.bus.cyc,
|
||||||
i_wb_stb = self.bus.stb,
|
i_wb_stb = self.bus.stb,
|
||||||
i_wb_we = self.bus.we,
|
i_wb_we = self.bus.we,
|
||||||
|
@ -203,6 +301,64 @@ class BRAM(Module):
|
||||||
o_wb_dat_r = self.bus.dat_r,
|
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
|
# Clock and Reset Generator
|
||||||
# I don't know how this works, I only know that it does.
|
# I don't know how this works, I only know that it does.
|
||||||
class _CRG(Module):
|
class _CRG(Module):
|
||||||
|
@ -240,10 +396,10 @@ class UpsilonSoC(SoCCore):
|
||||||
def add_ip(self, ip_str, ip_name):
|
def add_ip(self, ip_str, ip_name):
|
||||||
for seg_num, ip_byte in enumerate(ip_str.split('.'),start=1):
|
for seg_num, ip_byte in enumerate(ip_str.split('.'),start=1):
|
||||||
self.add_constant(f"{ip_name}{seg_num}", int(ip_byte))
|
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))
|
def add_picorv32(self):
|
||||||
# TODO: special name
|
self.submodules.picorv32 = pr = PicoRV32()
|
||||||
self.submodules.bram0 = BRAM(ClockSignal())
|
self.bus.add_region("picorv32_master_bram", pr.submodules.bram.region)
|
||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
variant="a7-100",
|
variant="a7-100",
|
||||||
|
@ -267,23 +423,11 @@ class UpsilonSoC(SoCCore):
|
||||||
Since Yosys doesn't support modern Verilog, only put preprocessed
|
Since Yosys doesn't support modern Verilog, only put preprocessed
|
||||||
(if applicable) files here.
|
(if applicable) files here.
|
||||||
"""
|
"""
|
||||||
platform.add_source("rtl/spi/spi_switch_preprocessed.v")
|
platform.add_source("rtl/picorv32/picorv32.v")
|
||||||
platform.add_source("rtl/spi/spi_master_preprocessed.v")
|
platform.add_source("rtl/spi/spi_master.v")
|
||||||
platform.add_source("rtl/spi/spi_master_no_write_preprocessed.v")
|
platform.add_source("rtl/spi/spi_master_ss.v")
|
||||||
platform.add_source("rtl/spi/spi_master_no_read_preprocessed.v")
|
platform.add_source("rtl/spi/spi_master_ss_wb.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/bram/bram.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 does not have sane defaults (no integrated rom)
|
||||||
SoCCore.__init__(self,
|
SoCCore.__init__(self,
|
||||||
|
@ -330,8 +474,6 @@ class UpsilonSoC(SoCCore):
|
||||||
self.add_ip(remote_ip, "REMOTEIP")
|
self.add_ip(remote_ip, "REMOTEIP")
|
||||||
self.add_constant("TFTP_SERVER_PORT", tftp_port)
|
self.add_constant("TFTP_SERVER_PORT", tftp_port)
|
||||||
|
|
||||||
self.add_bram("bram0")
|
|
||||||
|
|
||||||
# Add pins
|
# Add pins
|
||||||
platform.add_extension(io)
|
platform.add_extension(io)
|
||||||
self.submodules.base = Base(ClockSignal(), self.sdram, platform)
|
self.submodules.base = Base(ClockSignal(), self.sdram, platform)
|
||||||
|
|
Loading…
Reference in New Issue