more cleanup and bug finding
This commit is contained in:
parent
a10ad772bc
commit
4e3df09bb8
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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}
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
510
gateware/soc.py
510
gateware/soc.py
|
@ -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():
|
||||
|
|
Loading…
Reference in New Issue