more cleanup and bug finding

This commit is contained in:
Peter McGoron 2024-02-08 12:57:22 +00:00
parent a10ad772bc
commit 4e3df09bb8
12 changed files with 304 additions and 741 deletions

7
.gitignore vendored
View File

@ -35,17 +35,10 @@ gateware/csr_bitwidth.json
boot/*
!boot/boot.json
*.tar.gz
build/buildroot.tar.gz
build/buildroot/
build/f4pga/
build/litex/
build/opensbi/
build/upsilon/
build/upsilon.tar.gz
build/f4pga.tar.gz
build/opensbi.tar.gz
build/upsilon-opensbi.tar.gz
build/upsilon-hardware.tar.gz
build/upsilon-buildroot.tar.gz
swic/*.bin
swic/*.elf

View File

@ -30,6 +30,7 @@ COPY --chown=user:user f4pga.tar.gz /home/user
RUN tar -xvf f4pga.tar.gz \
&& rm f4pga.tar.gz \
&& cd f4pga/f4pga \
# && sed -i 's/-abc9//g' ./wrappers/tcl/xc7.f4pga.tcl \
&& bash -c 'source ~/conda/xc7/conda/etc/profile.d/conda.sh; conda activate xc7; pip install . ninja meson'
#LITEX

View File

@ -35,9 +35,9 @@ hardware-copy: upsilon-hardware.tar.gz
# XXX: this always fails if there are very high UIDs, but the
# file is copied successfully.
-docker cp upsilon-hardware.tar.gz upsilon-hardware:/home/user/upsilon-hardware.tar.gz
docker exec -ti upsilon-hardware tar -xvf upsilon-hardware.tar.gz
hardware-execute:
docker exec -ti upsilon-hardware /bin/bash -lc ' \
tar -xvf upsilon-hardware.tar.gz && \
cd upsilon/gateware && \
source ~/conda/xc7/conda/etc/profile.d/conda.sh && \
conda activate xc7 && \
@ -49,7 +49,7 @@ hardware-get:
docker cp upsilon-hardware:/home/user/upsilon/gateware/arty.dtb ../boot/
# docker cp upsilon-hardware:/home/user/upsilon/gateware/mmio.py ../boot/
docker cp upsilon-hardware:/home/user/upsilon/gateware/csr.json ../boot/
docker cp upsilon-hardware:/home/user/upsilon/gateware/picorv32.json ../boot/
# docker cp upsilon-hardware:/home/user/upsilon/gateware/picorv32.json ../boot/
hardware-clean:
-docker container stop upsilon-hardware
-docker container rm upsilon-hardware

View File

@ -8,19 +8,13 @@
DEVICETREE_GEN_DIR=.
all: rtl_codegen build/digilent_arty/digilent_arty.bit arty.dtb# mmio.py
rtl_codegen:
cd rtl && make
all: build/digilent_arty/digilent_arty.bit arty.dtb# mmio.py
csr.json build/digilent_arty/digilent_arty.bit: soc.py
python3 soc.py
clean:
rm -rf build csr.json arty.dts arty.dtb mmio.py
cd rtl && make clean
test:
cd rtl && make test
arty.dts: csr.json
litex_json2dts_linux csr.json > arty.dts

View File

@ -3,22 +3,12 @@
# For license terms, refer to the files in `doc/copying` in the Upsilon
# source distribution.
all: make_base make_spi make_control_loop make_bram
#all: make_bram
test:
cd control_loop && make test
make_base:
cd base && make codegen
make_spi:
cd spi && make codegen
make_control_loop:
cd control_loop && make codegen
make_waveform:
cd waveform && make codegen
make_bram:
cd bram && make codegen
clean:
cd base && make clean
cd spi && make clean
cd control_loop && make clean
cd waveform && make clean
dummy:
@echo empty
#make_bram:
# cd bram && make codegen
#clean:
# cd bram && make clean

View File

@ -1,9 +0,0 @@
.PHONY: lint
include ../common.makefile
codegen: base.v
base.v: base.v.m4
lint: base.v
verilator --lint-only base.v -I../spi -I../control_loop -I../waveform
clean:
rm -f base.v

View File

@ -1,396 +0,0 @@
m4_changequote(`⟨', `⟩')
m4_changecom(⟨/*⟩, ⟨*/⟩)
m4_define(generate_macro, ⟨m4_define(M4_$1, $2)⟩)
/*
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 is the module that collects all Verilog and exports a single interface
that is connected to the CPU by LiteX.
In this future, this module should be written into soc.py
Since yosys only allows for standard Verilog (no system verilog),
arrays (which would make everything much cleaner) cannot be used.
A preprocessor is used instead, and M4 is used because it is much
cleaner than the Verilog preprocessor (which is bad).
TODO: individual RST pins
*/
/*********************************************************/
/********************** M4 macros ************************/
/*********************************************************/
/* This macro is used in the module declaration.
* The first argument is the number of wires the select switch must
* support (2 for most DACs, 3 for the control loop DAC).
* The second argument is the DAC number.
*/
m4_define(m4_dac_wires, ⟨
input [$1-1:0] dac_sel_$2,
output dac_finished_$2,
input dac_arm_$2,
output [DAC_WID-1:0] dac_recv_buf_$2,
input [DAC_WID-1:0] dac_send_buf_$2
/*
input wf_arm_$2,
input wf_halt_on_finish_$2,
output wf_finished_$2,
input [WF_TIMER_WID-1:0] wf_time_to_wait_$2,
input wf_refresh_start_$2,
input [WF_RAM_WID-1:0] wf_start_addr_$2,
output wf_refresh_finished_$2,
output wf_running_$2,
output [WF_RAM_WID-1:0] wf_ram_dma_addr_$2,
input [WF_RAM_WORD_WID-1:0] wf_ram_word_$2,
output wf_ram_read_$2,
input wf_ram_valid_$2
*/
⟩)
/* Same thing but for ADCs */
m4_define(m4_adc_wires, ⟨
input [$3-1:0] adc_sel_$2,
output adc_finished_$2,
input adc_arm_$2,
output [$1-1:0] adc_recv_buf_$2
⟩)
/* This is used in the body of the module. It declares the interconnect
* for each DAC. The first argument is the amount of switch ports the
* DAC requires (2 for most DACs, 3 for the control loop DAC). The
* second argument is the DAC number.
*/
m4_define(m4_dac_switch, ⟨
wire [$1-1:0] mosi_port_$2;
wire [$1-1:0] miso_port_$2;
wire [$1-1:0] sck_port_$2;
wire [$1-1:0] ss_L_port_$2;
spi_switch #(
.PORTS($1)
) switch_$2 (
.select(dac_sel_$2),
.mosi(dac_mosi[$2]),
.miso(dac_miso[$2]),
.sck(dac_sck[$2]),
.ss_L(dac_ss_L[$2]),
.mosi_ports(mosi_port_$2),
.miso_ports(miso_port_$2),
.sck_ports(sck_port_$2),
.ss_L_ports(ss_L_port_$2)
);
spi_master_ss #(
.WID(DAC_WID),
.WID_LEN(DAC_WID_SIZ),
.CYCLE_HALF_WAIT(DAC_CYCLE_HALF_WAIT),
.TIMER_LEN(DAC_CYCLE_HALF_WAIT_SIZ),
.POLARITY(DAC_POLARITY),
.PHASE(DAC_PHASE),
.SS_WAIT(DAC_SS_WAIT),
.SS_WAIT_TIMER_LEN(DAC_SS_WAIT_SIZ)
) dac_master_$2 (
.clk(clk),
.rst_L(rst_L),
.mosi(mosi_port_$2[0]),
.miso(miso_port_$2[0]),
.sck_wire(sck_port_$2[0]),
.ss_L(ss_L_port_$2[0]),
.finished(dac_finished_$2),
.arm(dac_arm_$2),
.from_slave(dac_recv_buf_$2),
.to_slave(dac_send_buf_$2)
)
/*
waveform #(
.DAC_WID(DAC_WID),
.DAC_WID_SIZ(DAC_WID_SIZ),
.DAC_POLARITY(DAC_POLARITY),
.DAC_PHASE(DAC_PHASE),
.DAC_CYCLE_HALF_WAIT(DAC_CYCLE_HALF_WAIT),
.DAC_CYCLE_HALF_WAIT_SIZ(DAC_CYCLE_HALF_WAIT_SIZ),
.DAC_SS_WAIT(DAC_SS_WAIT),
.DAC_SS_WAIT_SIZ(DAC_SS_WAIT_SIZ),
.TIMER_WID(WF_TIMER_WID),
.WORD_WID(WF_WORD_WID),
.WORD_AMNT_WID(WF_WORD_AMNT_WID),
.WORD_AMNT(WF_WORD_AMNT),
.RAM_WID(WF_RAM_WID),
.RAM_WORD_WID(WF_RAM_WORD_WID),
.RAM_WORD_INCR(WF_RAM_WORD_INCR)
) waveform_$2 (
.clk(clk),
.arm(wf_arm_$2),
.halt_on_finish(wf_halt_on_finish_$2),
.running(wf_running_$2),
.finished(wf_finished_$2),
.time_to_wait(wf_time_to_wait_$2),
.refresh_start(wf_refresh_start_$2),
.start_addr(wf_start_addr_$2),
.refresh_finished(wf_refresh_finished_$2),
.ram_dma_addr(wf_ram_dma_addr_$2),
.ram_word(wf_ram_word_$2),
.ram_read(wf_ram_read_$2),
.ram_valid(wf_ram_valid_$2),
.mosi(mosi_port_$2[1]),
.sck(sck_port_$2[1]),
.ss_L(ss_L_port_$2[1])
)
*/
⟩)
/* Same thing but for ADCs */
m4_define(m4_adc_switch, ⟨
wire adc_mosi_unused_output_$2;
wire [$3-1:0] adc_mosi_port_$2; /* Unused! */
wire [$3-1:0] adc_sdo_port_$2;
wire [$3-1:0] adc_sck_port_$2;
wire [$3-1:0] adc_conv_L_port_$2;
spi_switch #(
.PORTS($3)
) adc_switch_$2 (
.select(adc_sel_$2),
.mosi(adc_mosi_unused_output_$2),
.miso(adc_sdo[$2]),
.sck(adc_sck[$2]),
.ss_L(adc_conv_L[$2]),
.mosi_ports(adc_mosi_port_$2),
.miso_ports(adc_sdo_port_$2),
.sck_ports(adc_sck_port_$2),
.ss_L_ports(adc_conv_L_port_$2)
);
spi_master_ss_no_write #(
.WID($1),
.WID_LEN(ADC_WID_SIZ),
.CYCLE_HALF_WAIT(ADC_CYCLE_HALF_WAIT),
.TIMER_LEN(ADC_CYCLE_HALF_WAIT_SIZ),
.SS_WAIT(ADC_CONV_WAIT),
.SS_WAIT_TIMER_LEN(ADC_CONV_WAIT_SIZ),
.POLARITY(ADC_POLARITY),
.PHASE(ADC_PHASE)
) adc_master_$2 (
.clk(clk),
.rst_L(rst_L),
.miso(adc_sdo_port_$2[0]),
.sck_wire(adc_sck_port_$2[0]),
.ss_L(adc_conv_L_port_$2[0]),
.finished(adc_finished_$2),
.arm(adc_arm_$2),
.from_slave(adc_recv_buf_$2)
);
/* 2nd option for each ADC is the non-converting option.
* This is used to flush output from reset ADCs.
* TODO: Lower power consumption by having SCK low while converter is
* not running? May require change to spi code.
*/
assign adc_sdo_port[1] = adc_sdo_port[0];
assign adc_sck_port[1] = adc_sck_port[0];
assign adc_conv_L_port[1] = 1;
⟩)
/*********************************************************/
/*********************** Verilog *************************/
/*********************************************************/
module base #(
parameter DAC_PORTS = 1,
m4_define(DAC_PORTS_CONTROL_LOOP, (DAC_PORTS + 1))
parameter DAC_NUM = 8, // Number of DACs
parameter DAC_WID = 24, // Bit width of DAC command
parameter DAC_DATA_WID = 20, // Bit with of DAC register
parameter DAC_WID_SIZ = 5, // number of bits required to store DAC_DATA_WID
parameter DAC_POLARITY = 0, // DAC SCK polarity
parameter DAC_PHASE = 1, // DAC SCK phase
parameter DAC_CYCLE_HALF_WAIT = 10,
parameter DAC_CYCLE_HALF_WAIT_SIZ = 4,
parameter DAC_SS_WAIT = 5,
parameter DAC_SS_WAIT_SIZ = 3,
parameter WF_TIMER_WID = 32,
parameter WF_WORD_WID = 20,
parameter WF_WORD_AMNT_WID = 11,
parameter [WF_WORD_AMNT_WID-1:0] WF_WORD_AMNT = 2047,
parameter WF_RAM_WID = 32,
parameter WF_RAM_WORD_WID = 16,
parameter WF_RAM_WORD_INCR = 2,
parameter ADC_PORTS = 2,
m4_define(ADC_PORTS_CONTROL_LOOP, (ADC_PORTS + 1))
parameter ADC_NUM = 8,
/* Three types of ADC. For now assume that their electronics
* are similar enough, just need different numbers for the width.
*/
parameter ADC_TYPE1_WID = 18,
parameter ADC_TYPE2_WID = 16,
parameter ADC_TYPE3_WID = 24,
parameter ADC_WID_SIZ = 5,
parameter ADC_CYCLE_HALF_WAIT = 5,
parameter ADC_CYCLE_HALF_WAIT_SIZ = 3,
parameter ADC_POLARITY = 1,
parameter ADC_PHASE = 0,
/* The ADC takes maximum 527 ns to capture a value.
* The clock ticks at 10 ns. Change for different clocks!
*/
parameter ADC_CONV_WAIT = 60,
parameter ADC_CONV_WAIT_SIZ = 6,
parameter CL_CONSTS_WHOLE = 21,
parameter CL_CONSTS_FRAC = 43,
parameter CL_CONSTS_SIZ = 7,
parameter CL_DELAY_WID = 16,
m4_define(CL_CONSTS_WID, (CL_CONSTS_WHOLE + CL_CONSTS_FRAC))
m4_define(CL_DATA_WID, CL_CONSTS_WID)
parameter CL_READ_DAC_DELAY = 5,
parameter CL_CYCLE_COUNT_WID = 18
) (
input clk,
input rst_L,
output [11-1:0] set_low,
output [DAC_NUM-1:0] dac_mosi,
input [DAC_NUM-1:0] dac_miso,
output [DAC_NUM-1:0] dac_sck,
output [DAC_NUM-1:0] dac_ss_L,
output [ADC_NUM-1:0] adc_conv,
input [ADC_NUM-1:0] adc_sdo,
output [ADC_NUM-1:0] adc_sck,
m4_dac_wires(DAC_PORTS_CONTROL_LOOP, 0),
m4_dac_wires(DAC_PORTS, 1),
m4_dac_wires(DAC_PORTS, 2),
m4_dac_wires(DAC_PORTS, 3),
m4_dac_wires(DAC_PORTS, 4),
m4_dac_wires(DAC_PORTS, 5),
m4_dac_wires(DAC_PORTS, 6),
m4_dac_wires(DAC_PORTS, 7),
m4_adc_wires(ADC_TYPE1_WID, 0, ADC_PORTS_CONTROL_LOOP),
m4_adc_wires(ADC_TYPE1_WID, 1, ADC_PORTS),
m4_adc_wires(ADC_TYPE1_WID, 2, ADC_PORTS),
m4_adc_wires(ADC_TYPE2_WID, 3, ADC_PORTS),
m4_adc_wires(ADC_TYPE2_WID, 4, ADC_PORTS),
m4_adc_wires(ADC_TYPE2_WID, 5, ADC_PORTS),
m4_adc_wires(ADC_TYPE3_WID, 6, ADC_PORTS),
m4_adc_wires(ADC_TYPE3_WID, 7, ADC_PORTS),
input cl_assert_change,
output cl_change_made,
output cl_in_loop,
input cl_run_loop_in,
input [ADC_TYPE1_WID-1:0] cl_setpt_in,
input [CL_DATA_WID-1:0] cl_P_in,
input [CL_DATA_WID-1:0] cl_I_in,
input [CL_DELAY_WID-1:0] cl_delay_in,
output [CL_CYCLE_COUNT_WID-1:0] cl_cycle_count,
output [DAC_DATA_WID-1:0] cl_z_pos,
output [ADC_TYPE1_WID-1:0] cl_z_measured
);
assign set_low = 0;
wire [ADC_NUM-1:0] adc_conv_L;
assign adc_conv = ~adc_conv_L;
m4_dac_switch(DAC_PORTS_CONTROL_LOOP, 0);
m4_dac_switch(DAC_PORTS, 1);
m4_dac_switch(DAC_PORTS, 2);
m4_dac_switch(DAC_PORTS, 3);
m4_dac_switch(DAC_PORTS, 4);
m4_dac_switch(DAC_PORTS, 5);
m4_dac_switch(DAC_PORTS, 6);
m4_dac_switch(DAC_PORTS, 7);
`define MAKE_TEST_CLOCK
`ifdef MAKE_TEST_CLOCK
reg [8-1:0] counter = 0;
always @ (posedge clk) begin
if (!rst_L) begin
counter <= 0;
test_clock <= 0;
end else begin
if (counter == ADC_CYCLE_HALF_WAIT) begin
counter <= 0;
test_clock <= !test_clock;
end else begin
counter <= counter + 1;
end
end
end
`endif
m4_adc_switch(ADC_TYPE1_WID, 0, ADC_PORTS_CONTROL_LOOP);
m4_adc_switch(ADC_TYPE1_WID, 1, ADC_PORTS);
m4_adc_switch(ADC_TYPE1_WID, 2, ADC_PORTS);
m4_adc_switch(ADC_TYPE1_WID, 3, ADC_PORTS);
m4_adc_switch(ADC_TYPE1_WID, 4, ADC_PORTS);
m4_adc_switch(ADC_TYPE1_WID, 5, ADC_PORTS);
m4_adc_switch(ADC_TYPE1_WID, 6, ADC_PORTS);
m4_adc_switch(ADC_TYPE1_WID, 7, ADC_PORTS);
control_loop #(
.ADC_WID(ADC_TYPE1_WID),
.ADC_WID_SIZ(ADC_WID_SIZ),
.ADC_CYCLE_HALF_WAIT(ADC_CYCLE_HALF_WAIT),
.ADC_CYCLE_HALF_WAIT_SIZ(ADC_CYCLE_HALF_WAIT_SIZ),
.ADC_POLARITY(ADC_POLARITY),
.ADC_PHASE(ADC_PHASE),
.ADC_CONV_WAIT(ADC_CONV_WAIT),
.ADC_CONV_WAIT_SIZ(ADC_CONV_WAIT_SIZ),
.CONSTS_WHOLE(CL_CONSTS_WHOLE),
.CONSTS_FRAC(CL_CONSTS_FRAC),
.CONSTS_SIZ(CL_CONSTS_SIZ),
.DELAY_WID(CL_DELAY_WID),
.READ_DAC_DELAY(CL_READ_DAC_DELAY),
.CYCLE_COUNT_WID(CL_CYCLE_COUNT_WID),
.DAC_WID(DAC_WID),
.DAC_WID_SIZ(DAC_WID_SIZ),
.DAC_DATA_WID(DAC_DATA_WID),
.DAC_POLARITY(DAC_POLARITY),
.DAC_PHASE(DAC_PHASE),
.DAC_CYCLE_HALF_WAIT(DAC_CYCLE_HALF_WAIT),
.DAC_CYCLE_HALF_WAIT_SIZ(DAC_CYCLE_HALF_WAIT_SIZ),
.DAC_SS_WAIT(DAC_SS_WAIT),
.DAC_SS_WAIT_SIZ(DAC_SS_WAIT_SIZ)
) cl (
.clk(clk),
.rst_L(rst_L),
.in_loop(cl_in_loop),
.dac_mosi(mosi_port_0[1]),
.dac_miso(miso_port_0[1]),
.dac_ss_L(ss_L_port_0[1]),
.dac_sck(sck_port_0[1]),
.adc_miso(adc_sdo_port_0[2]),
.adc_conv_L(adc_conv_L_port_0[2]),
.adc_sck(adc_sck_port_0[2]),
.assert_change(cl_assert_change),
.change_made(cl_change_made),
.run_loop_in(cl_run_loop_in),
.setpt_in(cl_setpt_in),
.P_in(cl_P_in),
.I_in(cl_I_in),
.delay_in(cl_delay_in),
.cycle_count(cl_cycle_count),
.z_pos(cl_z_pos),
.z_measured(cl_z_measured)
);
endmodule

View File

@ -1,15 +0,0 @@
# Copyright 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
# source distribution.
.PHONY: codegen
all: codegen
####### Codegen ########
include ../common.makefile
CODEGEN_FILES=bram.v
codegen: ${CODEGEN_FILES}
clean:
rm -rf obj_dir *.fst ${CODEGEN_FILES}

View File

@ -1,6 +1,3 @@
m4_changequote(`', `')
m4_changecom(/*, */)
/* Copyright 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
@ -25,9 +22,12 @@ module bram #(
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,
output reg [BUS_WID-1:0] wb_dat_r
);
initial wb_ack <= 0;
initial wb_dat_r <= 0;
/* When the size of the memory is a power of 2, the mask is the
* last addressable index in the array.
*
@ -38,33 +38,27 @@ module bram #(
* the 32 bit word that contains the address. This applies to halfwords
* and words as long as the accesses are aligned.
*/
(* ram_style = "block" *)
reg [WORD_WID-1:0] buffer [(ADDR_MASK >> 2):0];
/* Current index into the buffer. */
wire [13-1:0] ind = (wb_addr & ADDR_MASK) >> 2;
m4_define(bufwrite, begin
buffer[ind] <= (buffer[ind] & $1) | wb_dat_w[$2];
end)
always @ (posedge clk) if (wb_cyc && wb_stb && !wb_ack)
if (!wb_we) begin
wb_dat_r <= buffer[ind];
always @ (posedge clk) if (wb_cyc && wb_stb && !wb_ack) begin
wb_ack <= 1;
if (wb_we) begin
if (wb_sel[0])
buffer[ind][7:0] <= wb_dat_w[7:0];
if (wb_sel[1])
buffer[ind][15:8] <= wb_dat_w[15:8];
if (wb_sel[2])
buffer[ind][23:16] <= wb_dat_w[23:16];
if (wb_sel[3])
buffer[ind][31:24] <= wb_dat_w[31:24];
end else begin
wb_ack <= 1;
case (wb_sel)
4'b1111: buffer[ind] <= wb_dat_w;
4'b0011: bufwrite(32'hFFFF0000, 15:0)
4'b1100: bufwrite(32'h0000FFFF, 31:16)
4'b0001: bufwrite(32'hFFFFFF00, 7:0)
4'b0010: bufwrite(32'hFFFF00FF, 15:8)
4'b0100: bufwrite(32'hFF00FFFF, 23:16)
4'b1000: bufwrite(32'h00FFFFFF, 31:24)
default: ;
endcase
wb_dat_r <= buffer[ind];
end
else if (!wb_stb) begin
end else if (!wb_stb) begin
wb_ack <= 0;
end

View File

@ -2167,6 +2167,7 @@ module picorv32 #(
`endif
endmodule
`ifdef 0
// This is a simple example implementation of PICORV32_REGS.
// Use the PICORV32_REGS mechanism if you want to use custom
// memory resources to implement the processor register file.
@ -2315,7 +2316,9 @@ module picorv32_pcpi_mul #(
end
end
endmodule
`endif
`ifdef 0
module picorv32_pcpi_fast_mul #(
parameter EXTRA_MUL_FFS = 0,
parameter EXTRA_INSN_FFS = 0,
@ -2418,6 +2421,7 @@ endmodule
* picorv32_pcpi_div
***************************************************************/
`ifdef 0
module picorv32_pcpi_div (
input clk, resetn,
@ -2510,11 +2514,13 @@ module picorv32_pcpi_div (
end
endmodule
`endif
/***************************************************************
* picorv32_axi
***************************************************************/
`ifdef 0
module picorv32_axi #(
parameter [ 0:0] ENABLE_COUNTERS = 1,
parameter [ 0:0] ENABLE_COUNTERS64 = 1,
@ -2723,12 +2729,13 @@ module picorv32_axi #(
.trace_data (trace_data)
);
endmodule
`endif
/***************************************************************
* picorv32_axi_adapter
***************************************************************/
`ifdef 0
module picorv32_axi_adapter (
input clk, resetn,
@ -2807,7 +2814,7 @@ module picorv32_axi_adapter (
end
end
endmodule
`endif
/***************************************************************
* picorv32_wb

View File

@ -99,7 +99,7 @@ always @ (posedge clk) if (wb_cyc && wb_stb && !wb_ack) begin
default: ;
endcase
wb_ack <= 1;
end else begin
end else if (!wb_cyc) begin
wb_ack <= 0;
end

View File

@ -92,94 +92,92 @@ If there is more than one pin in the Pins string, the resulting
name will be a vector of pins.
"""
io = [
("differntial_output_low", 0, Pins("J17 J18 K15 J15 U14 V14 T13 U13 B6 E5 A3"), IOStandard("LVCMOS33")),
("dac_ss_L", 0, Pins("G13 D13 E15 F5 U12 D7 D4 E2"), IOStandard("LVCMOS33")),
("dac_mosi", 0, Pins("B11 B18 E16 D8 V12 D5 D3 D2"), IOStandard("LVCMOS33")),
("dac_miso", 0, Pins("A11 A18 D15 C7 V10 B7 F4 H2"), IOStandard("LVCMOS33")),
("dac_sck", 0, Pins("D12 K16 C15 E7 V11 E6 F3 G2"), IOStandard("LVCMOS33")),
("adc_conv", 0, Pins("V15 T11 N15 U18 U11 R10 R16 U17"), IOStandard("LVCMOS33")),
("adc_sck", 0, Pins("U16 R12 M16 R17 V16 R11 N16 T18"), IOStandard("LVCMOS33")),
("adc_sdo", 0, Pins("P14 T14 V17 P17 M13 R13 N14 R18"), IOStandard("LVCMOS33")),
# ("differntial_output_low", 0, Pins("J17 J18 K15 J15 U14 V14 T13 U13 B6 E5 A3"), IOStandard("LVCMOS33")),
("dac_ss_L_0", 0, Pins("G13"), IOStandard("LVCMOS33")),
("dac_mosi_0", 0, Pins("B11"), IOStandard("LVCMOS33")),
("dac_miso_0", 0, Pins("A11"), IOStandard("LVCMOS33")),
("dac_sck_0", 0, Pins("D12"), IOStandard("LVCMOS33")),
# ("dac_ss_L", 0, Pins("G13 D13 E15 F5 U12 D7 D4 E2"), IOStandard("LVCMOS33")),
# ("dac_mosi", 0, Pins("B11 B18 E16 D8 V12 D5 D3 D2"), IOStandard("LVCMOS33")),
# ("dac_miso", 0, Pins("A11 A18 D15 C7 V10 B7 F4 H2"), IOStandard("LVCMOS33")),
# ("dac_sck", 0, Pins("D12 K16 C15 E7 V11 E6 F3 G2"), IOStandard("LVCMOS33")),
# ("adc_conv", 0, Pins("V15 T11 N15 U18 U11 R10 R16 U17"), IOStandard("LVCMOS33")),
# ("adc_sck", 0, Pins("U16 R12 M16 R17 V16 R11 N16 T18"), IOStandard("LVCMOS33")),
# ("adc_sdo", 0, Pins("P14 T14 V17 P17 M13 R13 N14 R18"), IOStandard("LVCMOS33")),
("module_reset", 0, Pins("D9"), IOStandard("LVCMOS33")),
("test_clock", 0, Pins("P18"), IOStandard("LVCMOS33"))
# ("test_clock", 0, Pins("P18"), IOStandard("LVCMOS33"))
]
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. """
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``.
"""
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
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),
]
"""
Construct a combinatorial case statement. In verilog, the if
statment would look like
always @ (*) case (master_select)
1: begin
// Bus assignments...
end
2: begin
// Bus assignments...
end
// more cases:
default:
// assign everything to master 0
end
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.
The "assign_for_case" function constructs the body of the If
statement. It assigns all output ports to avoid latches.
"""
def assign_for_case(i):
asn = [ ]
for j in range(masters_len):
asn += [
self.buses[i].cyc.eq(self.slave.bus.cyc if i == j else 0),
self.buses[i].stb.eq(self.slave.bus.stb if i == j else 0),
self.buses[i].we.eq(self.slave.bus.we if i == j else 0),
self.buses[i].sel.eq(self.slave.bus.sel if i == j else 0),
self.buses[i].adr.eq(self.slave.bus.adr if i == j else 0),
self.buses[i].dat_w.eq(self.slave.bus.dat_w if i == j else 0),
self.buses[i].ack.eq(self.slave.bus.ack if i == j else 0),
self.buses[i].dat_r.eq(self.slave.bus.dat_r if i == j else 0),
self.buses[i].cti.eq(0),
self.buses[i].bte.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.storage, cases)
# 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. """
#
# 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``.
# """
#
# 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
#
# 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"))
#
# """
# Construct a combinatorial case statement. In verilog, the if
# statment would look like
#
# always @ (*) case (master_select)
# 1: begin
# // Bus assignments...
# end
# 2: begin
# // Bus assignments...
# end
# // more cases:
# default:
# // assign everything to master 0
# end
#
# 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.
#
# The "assign_for_case" function constructs the body of the If
# statement. It assigns all output ports to avoid latches.
# """
#
# def assign_for_case(i):
# asn = [ ]
#
# for j in range(masters_len):
# asn += [
# self.buses[i].cyc.eq(self.slave.bus.cyc if i == j else 0),
# self.buses[i].stb.eq(self.slave.bus.stb if i == j else 0),
# self.buses[i].we.eq(self.slave.bus.we if i == j else 0),
# self.buses[i].sel.eq(self.slave.bus.sel if i == j else 0),
# self.buses[i].adr.eq(self.slave.bus.adr if i == j else 0),
# self.buses[i].dat_w.eq(self.slave.bus.dat_w if i == j else 0),
# self.buses[i].ack.eq(self.slave.bus.ack if i == j else 0),
# self.buses[i].dat_r.eq(self.slave.bus.dat_r if i == j else 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.storage, cases)
class SPIMaster(Module):
def __init__(self, rst, miso, mosi, sck, ss,
@ -192,24 +190,24 @@ class SPIMaster(Module):
spi_cycle_half_wait = 1,
):
self.bus = Interface(data_width = 32, address_width=32, addressing="word")
self.region = SoCIORegion(size=0x10, cached=False)
self.bus = Interface(data_width = 32, address_width=32, addressing="byte")
self.region = SoCRegion(size=0x10, cached=False)
self.comb += [
self.bus.cti.eq(0),
self.bus.bte.eq(0),
self.bus.err.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,
p_SS_WAIT = ss_wait,
p_SS_WAIT_TIMER_LEN = minbits(ss_wait),
p_CYCLE_HALF_WAIT = spi_cycle_half_wait,
p_TIMER_LEN = minbits(spi_cycle_half_wait),
p_WID = spi_wid,
p_WID_LEN = minbits(spi_wid),
p_ENABLE_MISO = enable_miso,
p_ENABLE_MOSI = enable_mosi,
p_POLARITY = polarity,
p_PHASE = phase,
i_clk = ClockSignal(),
i_rst_L = rst,
@ -229,158 +227,150 @@ class SPIMaster(Module):
)
# 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(0x17), cached=False)
self.comb += [
self.bus.cti.eq(0),
self.bus.bte.eq(0),
]
self.sync += [
If(self.bus.cyc == 1 and self.bus.stb == 1 and self.bus.ack == 0,
Case(self.bus.adr[0:4], {
0x0: self.bus.dat_r.eq(self.cl_I.storage),
0x4: self.bus.dat_r.eq(self.cl_P.storage),
0x8: self.bus.dat_r.eq(self.deltaT.storage),
0xC: self.bus.dat_r.eq(self.setpt.storage),
0x10: If(self.bus.we,
self.zset.status.eq(self.bus.dat_w)
).Else(
self.bus.dat_r.eq(self.zset.status)
),
0x14: If(self.bus.we,
self.zpos.status.eq(self.bus.dat_w),
).Else(
self.bus.dat_r.eq(self.zpos.status)
),
}),
self.bus.ack.eq(1),
).Else(
self.bus.ack.eq(0),
)
]
class BRAM(Module):
""" 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, origin=None):
"""
:param addr_mask: Mask which defines the amount of bytes accessable
by the BRAM.
:param origin: Origin of the BRAM module region. This is seen by the
subordinate master, not the usual master.
"""
self.bus = Interface(data_width=32, address_width=32, addressing="byte")
# Non-IO (i.e. MMIO) regions need to be cached
self.region = SoCRegion(origin=origin, size=addr_mask+1, cached=True)
self.comb += [
self.bus.cti.eq(0),
self.bus.bte.eq(0),
]
self.specials += Instance("bram",
p_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,
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,
)
class PicoRV32(Module, AutoCSR):
def __init__(self, bramwid=0x1000):
self.submodules.params = params = ControlLoopParameters()
self.submodules.bram = self.bram = bram = BRAM(bramwid-1, origin=0x10000)
self.submodules.bram_iface = self.bram_iface = bram_iface = PreemptiveInterface(2, bram)
# This is the PicoRV32 master
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")
self.ic = 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=0x10000, mode="ro", cached=True),
"picorv32_io": SoCIORegion(origin=0x100000, size=0x100, mode="rw", cached=False),
},
)
ic.add_slave("picorv32_bram", bram_iface.buses[1], bram.region)
ic.add_slave("picorv32_params", params.bus, params.region)
ic.add_master("picorv32_master", self.masterbus)
# NOTE: need to compile to these extact instructions
self.specials += Instance("picorv32_wb",
p_COMPRESSED_ISA = 1,
p_ENABLE_MUL = 1,
p_PROGADDR_RESET=0x10000,
p_PROGADDR_IRQ=0x100010,
p_REGS_INIT_ZERO = 1,
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),
)
def do_finalize(self):
self.ic.finalize()
jsondata = {}
for region in self.ic.regions:
d = self.ic.regions[region]
jsondata[region] = {
"origin": d.origin,
"size": d.size,
}
with open('picorv32.json', 'w') as f:
import json
json.dump(jsondata, f)
# 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(0x17), cached=False)
# self.sync += [
# If(self.bus.cyc == 1 and self.bus.stb == 1 and self.bus.ack == 0,
# Case(self.bus.adr[0:4], {
# 0x0: self.bus.dat_r.eq(self.cl_I.storage),
# 0x4: self.bus.dat_r.eq(self.cl_P.storage),
# 0x8: self.bus.dat_r.eq(self.deltaT.storage),
# 0xC: self.bus.dat_r.eq(self.setpt.storage),
# 0x10: If(self.bus.we,
# self.zset.status.eq(self.bus.dat_w)
# ).Else(
# self.bus.dat_r.eq(self.zset.status)
# ),
# 0x14: If(self.bus.we,
# self.zpos.status.eq(self.bus.dat_w),
# ).Else(
# self.bus.dat_r.eq(self.zpos.status)
# ),
# }),
# self.bus.ack.eq(1),
# ).Else(
# self.bus.ack.eq(0),
# )
# ]
#
# class BRAM(Module):
# """ 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, origin=None):
# """
# :param addr_mask: Mask which defines the amount of bytes accessable
# by the BRAM.
# :param origin: Origin of the BRAM module region. This is seen by the
# subordinate master, not the usual master.
# """
# self.bus = Interface(data_width=32, address_width=32, addressing="byte")
#
# # Non-IO (i.e. MMIO) regions need to be cached
# self.region = SoCRegion(origin=origin, size=addr_mask+1, cached=True)
#
# self.specials += Instance("bram",
# p_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,
# 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,
# )
#
# class PicoRV32(Module, AutoCSR):
# def __init__(self, bramwid=0x1000):
# self.submodules.params = params = ControlLoopParameters()
# self.submodules.bram = self.bram = bram = BRAM(bramwid-1, origin=0x10000)
# self.submodules.bram_iface = self.bram_iface = bram_iface = PreemptiveInterface(2, bram)
#
# # This is the PicoRV32 master
# 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")
#
# self.ic = 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=0x10000, mode="ro", cached=True),
# "picorv32_io": SoCIORegion(origin=0x100000, size=0x100, mode="rw", cached=False),
# },
# )
#
# ic.add_slave("picorv32_bram", bram_iface.buses[1], bram.region)
# ic.add_slave("picorv32_params", params.bus, params.region)
# ic.add_master("picorv32_master", self.masterbus)
#
# # NOTE: need to compile to these extact instructions
# self.specials += Instance("picorv32_wb",
# p_COMPRESSED_ISA = 1,
# p_ENABLE_MUL = 1,
# p_PROGADDR_RESET=0x10000,
# p_PROGADDR_IRQ=0x100010,
# p_REGS_INIT_ZERO = 1,
# 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),
# )
#
# def do_finalize(self):
# self.ic.finalize()
# jsondata = {}
#
# for region in self.ic.regions:
# d = self.ic.regions[region]
# jsondata[region] = {
# "origin": d.origin,
# "size": d.size,
# }
#
# with open('picorv32.json', 'w') as f:
# import json
# json.dump(jsondata, f)
# Clock and Reset Generator
# I don't know how this works, I only know that it does.
@ -423,7 +413,11 @@ class UpsilonSoC(SoCCore):
def add_picorv32(self):
self.submodules.picorv32 = pr = PicoRV32()
self.bus.add_slave("picorv32_master_bram", pr.bram_iface.buses[0],
SoCRegion(size=pr.bram.region.size, cached=True))
SoCRegion(origin=None,size=pr.bram.region.size, cached=True))
def add_bram(self):
self.submodules.bram = br = BRAM(0x1FF)
self.bus.add_slave("bram", br.bus, br.region)
def __init__(self,
variant="a7-100",
@ -456,7 +450,7 @@ class UpsilonSoC(SoCCore):
# SoCCore does not have sane defaults (no integrated rom)
SoCCore.__init__(self,
clk_freq=sys_clk_freq,
toolchain="f4pga",
toolchain="symbiflow",
platform = platform,
bus_standard = "wishbone",
ident = f"Arty-{variant} F4PGA LiteX VexRiscV Zephyr - Upsilon",
@ -500,6 +494,16 @@ class UpsilonSoC(SoCCore):
# Add pins
platform.add_extension(io)
self.submodules.spi0 = SPIMaster(
platform.request("module_reset"),
platform.request("dac_miso_0"),
platform.request("dac_mosi_0"),
platform.request("dac_sck_0"),
platform.request("dac_ss_L_0"),
)
self.bus.add_slave("spi0", self.spi0.bus, self.spi0.region)
#self.add_bram()
#self.add_picorv32()
def main():