some more changes
This commit is contained in:
parent
5125719a1f
commit
029cc53c5f
|
@ -0,0 +1,64 @@
|
|||
# Functions for converting to and from fixed point in Python.
|
||||
from math import log10, floor
|
||||
|
||||
def string_to_fixed_point(s, fracnum):
|
||||
l = s.split('.')
|
||||
if len(l) == 1:
|
||||
return int(s) << fracnum
|
||||
elif len(l) != 2:
|
||||
return None
|
||||
|
||||
dec = 10
|
||||
frac = 0
|
||||
|
||||
frac_decimal = int(l[1])
|
||||
# get the smallest power of ten higher then frac_decimal
|
||||
frac_decimal_len = floor(log10(frac_decimal) + 1)
|
||||
pow10 = 10**frac_decimal_len
|
||||
frac = 0
|
||||
|
||||
# Example:
|
||||
# 0.4567 = 0.abcdefgh...
|
||||
# where abcdefgh are binary digits.
|
||||
# multiply both sides by two:
|
||||
# 0.9134 = a.bcdefgh ...
|
||||
# therefore a = 0. Then remove the most significant digit.
|
||||
# Then multiply by 2 again. Then
|
||||
# 18268 = b.cdefgh ...
|
||||
# therefore b = 1. Then take 8268, and so on.
|
||||
for i in range(0,fracnum):
|
||||
frac_decimal = frac_decimal * 2
|
||||
div, mod = divmod(frac_decimal, pow10)
|
||||
frac = div | (frac << 1)
|
||||
frac_decimal = mod
|
||||
|
||||
whole = int(l[0])
|
||||
if whole < 0:
|
||||
return -((-whole) << fracnum | frac)
|
||||
else:
|
||||
return whole << fracnum | frac
|
||||
|
||||
def fixed_point_to_string(fxp, fracnum):
|
||||
whole = str(fxp >> fracnum)
|
||||
mask = (1 << fracnum) - 1
|
||||
fracbit = fxp & mask
|
||||
n = 1
|
||||
frac = ""
|
||||
|
||||
if fracbit == 0:
|
||||
return whole
|
||||
|
||||
# The same method can be applied backwards.
|
||||
# 0.1110101 = 0.abcdefgh ...
|
||||
# where abcdefgh... are decimal digits. Then multiply by 10 to
|
||||
# get
|
||||
# 1001.0010010 = a.bcdefgh ...
|
||||
# therefore a = 0b1001 = 9. Then use a bitmask to get
|
||||
# 0.0010010 = 0.bcdefgh ...
|
||||
# etc.
|
||||
|
||||
for i in range(0, fracnum):
|
||||
fracbit = fracbit * 10
|
||||
frac = frac + str(fracbit >> fracnum)
|
||||
fracbit = fracbit & mask
|
||||
return whole + "." + frac
|
|
@ -1,3 +1,5 @@
|
|||
/* TODO: move SPI masters out of the control loop design */
|
||||
|
||||
/************ Introduction to PI Controllers
|
||||
* The continuous form of a PI loop is
|
||||
*
|
||||
|
@ -129,16 +131,17 @@ module control_loop
|
|||
|
||||
output adc_sck,
|
||||
input adc_in,
|
||||
output adc_conv,
|
||||
output adc_conv, // active high
|
||||
|
||||
output dac_sck,
|
||||
output dac_ss,
|
||||
output dac_ss, // active high
|
||||
output dac_out,
|
||||
|
||||
/* Informational output.
|
||||
* These registers are also used for storing information while
|
||||
* the loop is running.
|
||||
*/
|
||||
|
||||
output signed [ERR_WID-1:0] err_cur,
|
||||
output signed [CONSTS_WID-1:0] adj,
|
||||
|
||||
|
@ -375,20 +378,24 @@ always @ (posedge clk) begin
|
|||
state <= WAIT_ON_ADC;
|
||||
timer <= 0;
|
||||
adc_arm <= 1;
|
||||
adc_conv <= 1;
|
||||
end
|
||||
end
|
||||
WAIT_ON_ADC: if (adc_finished) begin
|
||||
adc_arm <= 0;
|
||||
adc_conv <= 0;
|
||||
arm_mul <= 1;
|
||||
state <= WAIT_ON_MUL;
|
||||
end
|
||||
WAIT_ON_MUL: if (mul_finished) begin
|
||||
arm_mul <= 0;
|
||||
dac_arm <= 1;
|
||||
dac_ss <= 1;
|
||||
state <= WAIT_ON_DAC;
|
||||
end
|
||||
WAIT_ON_DAC: if (dac_finished) begin
|
||||
state <= WAIT_ON_ARM;
|
||||
dac_ss <= 0;
|
||||
dac_arm <= 0;
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
#include <memory>
|
||||
#include <limits>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <verilated.h>
|
||||
#include "Vcontrol_loop.h"
|
||||
|
@ -19,21 +22,190 @@ Vcontrol_loop *mod;
|
|||
*/
|
||||
|
||||
class Transfer {
|
||||
std::random_device rd;
|
||||
std::default_random_engine generator;
|
||||
std::normal_distribution dist;
|
||||
double scale;
|
||||
|
||||
double sample() {return scale*dist(rd);}
|
||||
|
||||
public:
|
||||
Transfer(double scale, double mean, double dev, double m, double b)
|
||||
: scale{scale}, rd{}, dist{mean,dev} {}
|
||||
Transfer(double scale, double mean, double dev, double m, double b, int seed)
|
||||
: scale{scale}, dist{mean,dev}, generator{} {
|
||||
if (seed < 0) {
|
||||
std::random_device rd;
|
||||
generator.seed(rd());
|
||||
} else {
|
||||
generator.seed(seed);
|
||||
}
|
||||
}
|
||||
|
||||
double val(double x) {
|
||||
return m*x + b + sample();
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
/* Each constant is 48 bits long, with 15 whole bits.
|
||||
constexpr auto CONSTS_WHOLE_WID = 15;
|
||||
constexpr auto CONSTS_WID = 48;
|
||||
constexpr auto CONSTS_FRAC_WID = CONSTS_WID - CONSTS_WHOLE_WID;
|
||||
constexpr auto CONSTS_FRAC_MASK = (1 << CONSTS_FRAC_WID) - 1;
|
||||
|
||||
constexpr uint64_t fractional_base_conv(uint64_t input) {
|
||||
/* Fractional base conversion algorithm.
|
||||
Given an integer in base M (i.e. 10) there is an expansion in base N (i.e. 2):
|
||||
0.abcdefgh... = 0.ijklmnop...
|
||||
where abcdefgh... are in base M and ijklmnop... are in base N. The algorithm
|
||||
computes the digits in base N.
|
||||
|
||||
Multiply the converted number by N. Then there are new numbers:
|
||||
A.BCDEFGH... = i.jklmnop...
|
||||
Since 0.abcdefgh < 1, A.BCDEFGH < N. Therefore
|
||||
the digit "A" must be a number less than N. Then i = A. Cutting off all the
|
||||
other digits,
|
||||
0.BCDEFGH... = 0.jklmnop...
|
||||
continue until there are no more digits left.
|
||||
*/
|
||||
|
||||
/* Calculate the lowest power of 10 greater than input.
|
||||
This can be done with logarithms, but floating point is not available on
|
||||
some embedded platforms. This makes the code more portable.
|
||||
*/
|
||||
uint64_t pow10 = 1;
|
||||
while (input / pow10 > 0)
|
||||
pow10 *= 10;
|
||||
|
||||
uint64_t out = 0;
|
||||
for (unsigned i = 0; i < CONSTS_FRAC_WID; i++) {
|
||||
input *= 2;
|
||||
uint64_t dig = input / pow10, mod = input % pow10;
|
||||
out = dig | (out << 1);
|
||||
input = mod;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static int64_t multiply_unity(uint64_t i, int sign) {
|
||||
if (sign > 0) {
|
||||
return std::reinterpret_cast<int64_t>(i);
|
||||
} else {
|
||||
return std::reinterpret_cast<int64_t>(~i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
constexpr uint64_t SCALE_WHOLE = 12820;
|
||||
constexpr uint64_t SCALE_FRAC = fractional_base_conv(51282051282);
|
||||
constexpr uint64_t SCALE_NUM = (SCALE_WHOLE << CONSTS_FRAC_WID) | SCALE_FRAC;
|
||||
|
||||
static int64_t signed_to_fxp(char *s) {
|
||||
// Skip whitespace.
|
||||
while (isspace(*c++));
|
||||
// Check if number is negative.
|
||||
int sign = 1;
|
||||
if (*s == '-') {
|
||||
pos = -1;
|
||||
s++;
|
||||
}
|
||||
|
||||
// Split the number into whole and fractional components.
|
||||
char *p = strchr(s, '.');
|
||||
if (!p)
|
||||
return multiply_unity(strtoull(s, NULL, 10), sign);
|
||||
*p = 0;
|
||||
// s now points to a NUL terminated string with the whole number
|
||||
// component.
|
||||
uint64_t whole = strtoull(s, NULL, 10);
|
||||
|
||||
p++;
|
||||
// p is the start of the fractional component.
|
||||
uint64_t frac_decimal = strtoull(p, NULL, 10);
|
||||
uint64_t final = ((whole << CONSTS_FRAC_WID) | fractional_base_conv(frac_decimal, CONSTS_FRAC_WID))
|
||||
* SCALE_NUM;
|
||||
return multiply_unity(final, sign);
|
||||
}
|
||||
|
||||
static std::string fxp_to_str(int64_t inum, unsigned decdigs) {
|
||||
std::string s = "";
|
||||
uint64_t num;
|
||||
|
||||
if (inum < 0) {
|
||||
num = std::reinterpret_cast<uint64_t>(~inum) + 1;
|
||||
s.insert(0,1, '-');
|
||||
} else {
|
||||
num = std::reinterpet_cast<uint64_t>(num);
|
||||
}
|
||||
|
||||
s += std::to_string(num >> CONSTS_FRAC_WID);
|
||||
|
||||
int64_t frac = num & CONSTS_FRAC_MASK;
|
||||
if (frac == 0 || decdigs == 0)
|
||||
return;
|
||||
|
||||
s += ".";
|
||||
|
||||
/* Applying the algorithm in fractional_base_conv() backwards. */
|
||||
while (decdigs > 0 && frac != 0) {
|
||||
num *= 2;
|
||||
s += std::to_string(num >> CONSTS_FRAC_WID);
|
||||
num = num & CONSTS_FRAC_WID;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static int64_t I_const, dt_const, P_const, setpt;
|
||||
static unsigned long seed, ;
|
||||
|
||||
static void usage(char *argv0, int code) {
|
||||
std::cout << argv0 << " -I I -t dt -d delay -s seed -S setpt -P p [+verilator...]" << std::endl;
|
||||
exit(code);
|
||||
}
|
||||
|
||||
static void parse_args(int argc, char *argv[]) {
|
||||
const char *optstring = "I:t:s:P:h";
|
||||
int opt;
|
||||
Verilated::commandArgs(argc, argv);
|
||||
|
||||
while ((opt = getopt(argc, argv, optstring)) != -1) {
|
||||
switch (opt) {
|
||||
case 'I':
|
||||
I_const = signed_to_fxp(optarg);
|
||||
break;
|
||||
case 't':
|
||||
dt_const = signed_to_fxp(optarg);
|
||||
break;
|
||||
case 'S':
|
||||
setpt = signed_to_fxp(optarg);
|
||||
break;
|
||||
case 's':
|
||||
seed = strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'P':
|
||||
P_const = strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'd':
|
||||
dely = strtoul(optarg, NULL, 10);
|
||||
break;
|
||||
case 'h':
|
||||
usage(argv[0], 0);
|
||||
break;
|
||||
default:
|
||||
usage(argv[1], 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vtop *mod;
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
parse_args(argc, argv);
|
||||
mod = new Vtop;
|
||||
|
||||
mod->clk = 0;
|
||||
mod->arm = 1;
|
||||
mod->setpt = setpt;
|
||||
mod->alpha = I_const * dt_const + P_const;
|
||||
mod->cl_p = P_const;
|
||||
mod->dy = 5;
|
||||
}
|
||||
|
|
|
@ -7,10 +7,12 @@ module top
|
|||
parameter DAC_DATA_WID = 20,
|
||||
parameter CONSTS_WID = 48,
|
||||
parameter DELAY_WID = 16
|
||||
(
|
||||
)(
|
||||
input clk,
|
||||
input signed [ADC_WID-1:0] read_data,
|
||||
output signed [DAC_WID-1:0] write_data,
|
||||
input arm,
|
||||
|
||||
input signed [ADC_WID-1:0] measured_data,
|
||||
output signed [DAC_WID-1:0] output_data,
|
||||
input signed [ADC_WID-1:0] setpt,
|
||||
input signed [CONSTS_WID-1:0] alpha,
|
||||
input signed [CONSTS_WID-1:0] cl_p,
|
||||
|
@ -22,21 +24,79 @@ module top
|
|||
|
||||
wire adc_sck;
|
||||
wire adc_ss;
|
||||
wire adc_mosi;
|
||||
wire adc_miso;
|
||||
reg adc_finished = 0;
|
||||
|
||||
wire dac_mosi;
|
||||
wire dac_sck;
|
||||
wire dac_ss;
|
||||
reg dac_finished = 0;
|
||||
|
||||
/* Emulate a control loop environment with simulator controlled
|
||||
SPI interfaces.
|
||||
*/
|
||||
|
||||
/* ADC */
|
||||
spi_slave_no_write #(
|
||||
.WID(ADC_WID),
|
||||
.WID(5),
|
||||
.
|
||||
.WID_LEN(5),
|
||||
.ADC_POLARITY(ADC_POLARITY),
|
||||
.ADC_PHASE(ADC_PHASE)
|
||||
)(
|
||||
.clk(clk),
|
||||
.to_master(measured_data),
|
||||
.sck(adc_sck),
|
||||
.ss_L(!adc_ss),
|
||||
.miso(adc_miso),
|
||||
.rdy(!dac_ss),
|
||||
.finished(adc_finished)
|
||||
);
|
||||
|
||||
/* DAC */
|
||||
spi_slave_no_read #(
|
||||
.WID(DAC_WID),
|
||||
.WID_LEN(5),
|
||||
.DAC_POLARITY(DAC_POLARITY),
|
||||
.DAC_PHASE(DAC_PHASE)
|
||||
)(
|
||||
.clk(clk),
|
||||
.from_master(output_data),
|
||||
.mosi(dac_mosi),
|
||||
.sck(dac_sck),
|
||||
.ss_L(!dac_ss),
|
||||
.rdy(!dac_ss),
|
||||
.finished(dac_finished)
|
||||
);
|
||||
|
||||
control_loop #(
|
||||
.ADC_WID(ADC_WID),
|
||||
.DAC_WID(DAC_WID),
|
||||
.DAC_DATA_WID(DAC_DATA_WID),
|
||||
.CONSTS_WID(CONSTS_WID),
|
||||
.DELAY_WID(DELAY_WID)
|
||||
.DELAY_WID(DELAY_WID),
|
||||
.ADC_POLARITY(ADC_POLARITY),
|
||||
.ADC_PHASE(ADC_PHASE),
|
||||
.DAC_POLARITY(DAC_POLARITY),
|
||||
.DAC_PHASE(DAC_PHASE)
|
||||
) cloop (
|
||||
.clk(clk),
|
||||
.arm(arm),
|
||||
|
||||
.adc_sck(adc_sck),
|
||||
.adc_in(adc_miso),
|
||||
.adc_conv(adc_ss),
|
||||
|
||||
.dac_sck(dac_sck),
|
||||
.dac_ss(dac_ss),
|
||||
.dac_out(dac_mosi),
|
||||
|
||||
.setpt_in(setpt),
|
||||
.cl_alpha_in(alpha),
|
||||
.cl_p_in(cl),
|
||||
.delay_in(dy),
|
||||
|
||||
.err(err_cur),
|
||||
.adj(adj)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
module raster #(
|
||||
parameter SAMPLEWID = 9,
|
||||
parameter DAC_DATA_WID = 20,
|
||||
parameter DAC_WID = 24,
|
||||
parameter STEPWID = 16,
|
||||
parameter MAX_ADC_DATA_WID = 24
|
||||
) (
|
||||
input clk,
|
||||
input arm,
|
||||
|
||||
/* Amount of steps per sample. */
|
||||
input [STEPWID-1:0] steps,
|
||||
/* Amount of samples in one line (forward) */
|
||||
input [SAMPLEWID-1:0] samples,
|
||||
/* Amount of lines in the output. */
|
||||
input [SAMPLEWID-1:0] lines,
|
||||
|
||||
/* Each step goes (x,y) -> (dx,dy) forward for each line of
|
||||
* the output. */
|
||||
input signed [DAC_DATA_WID-1:0] dx,
|
||||
input signed [DAC_DATA_WID-1:0] dy,
|
||||
|
||||
/* Vertical steps to go to the next line. */
|
||||
input signed [DAC_DATA_WID-1:0] dx_vert,
|
||||
input signed [DAC_DATA_WID-1:0] dy_vert,
|
||||
|
||||
/* X and Y DAC piezos */
|
||||
input x_ready,
|
||||
output [DACWID-1:0] x_to_dac,
|
||||
input [DACWID-1:0] x_from_dac,
|
||||
output x_finished,
|
||||
|
||||
input y_ready,
|
||||
output [DACWID-1:0] y_to_dac,
|
||||
input [DACWID-1:0] y_from_dac,
|
||||
output y_finished,
|
||||
|
||||
/* Connections to all possible ADCs. These are connected to SPI masters
|
||||
* and they will automatically extend ADC value lengths to their highest
|
||||
* values. */
|
||||
input adc_in [0:ADCNUM-1],
|
||||
output [MAX_ADC_DATA_WID-1:0] adc_conv [0:ADCNUM-1],
|
||||
output adc_finished [0:ADCNUM-1],
|
||||
|
||||
/* Bitmap for which ADCs are used. */
|
||||
input [ADCNUM-1:0] adc_used,
|
||||
|
||||
output signed [MAX_ADC_DATA_WID-1:0] fifo_data,
|
||||
output fifo_ready,
|
||||
input fifo_valid
|
||||
);
|
||||
|
||||
/* State machine:
|
||||
┏━━━━ WAIT ON ARM
|
||||
↑ ↓ (arm -> 1)
|
||||
┃ GET DAC VALUES
|
||||
┃ ↓ (when x and y values are obtained)
|
||||
┃ ┏━LOOP FORWARD WITHOUT MEASUREMENT
|
||||
┃ ↑ ↓ (when enough steps are taken)
|
||||
┃ ┃ GET ADC VALUES
|
||||
┃ ┃ ↓ (when all ADC values are obtained)
|
||||
┃ ┃ SEND THROUGH FIFO
|
||||
┃ ┃ ↓ (when finished)
|
||||
┃ ┏━┫ ┃
|
||||
┃ ↑ ┗━━━←━┫
|
||||
┃ ┃ ┃ (when at the end of a line)
|
||||
┃ ┃ ┃
|
||||
┃ ┃ ┏━LOOP BACKWARD WITHOUT MEASUREMENT
|
||||
┃ ┃ ↑ ↓ (when enough steps are taken)
|
||||
┃ ┃ ┃ GET ADC VALUES, BACKWARDS MEASUREMENT
|
||||
┃ ┃ ┃ ↓ (when all ADC values are obtained)
|
||||
┃ ┃ ┃ SEND THROUGH FIFO, BACKWARDS MEASUREMENT
|
||||
┃ ┃ ┃ ↓ (when finished)
|
||||
┃ ┃ ┃ ┃
|
||||
┃ ┃ ┗━━━←━┫
|
||||
┃ ┃ ↓
|
||||
┃ ┗━━━━━━━┫
|
||||
┃ ↓ (when the image is finished)
|
||||
┃ ┃
|
||||
┃ WAIT FOR ARM DEASSERT
|
||||
┃ ↓ (when arm = 0)
|
||||
┗━━━━━━━━━┛
|
||||
*/
|
||||
|
||||
localparam WAIT_ON_ARM = 0;
|
||||
localparam GET_DAC_VALUES = 1;
|
||||
localparam INCREMENT_XVAL = 2;
|
||||
localparam GET_ADC_VAL = 3;
|
||||
localparam SEND_FIFO = 4;
|
||||
localparam WAIT_FOR_REARM = 5;
|
||||
|
||||
reg [2:0] stepstate = WAIT_ON_ARM;
|
||||
reg is_forward = 1;
|
||||
reg [SAMPLEWID-1:0] samplenum;
|
||||
reg [STEPWID-1:0] stepnum;
|
||||
|
||||
always @ (posedge clk) begin
|
||||
|
||||
end
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,19 @@
|
|||
module sign_extend #(
|
||||
parameter WID1 = 18,
|
||||
parameter WID2 = 24
|
||||
) (
|
||||
input signed [WID1-1:0] b1,
|
||||
output signed [WID2-1:0] b2
|
||||
);
|
||||
|
||||
assign b2[WID1-1:0] = b1;
|
||||
/* Assign the high bits of b2 to be the extension of the
|
||||
* highest bit of b1. If the MSB of b1 is 1 (i.e. b1 is
|
||||
* negative), then all high bits of b2 must be negative.
|
||||
* If the MSB of b1 is 0, then the high bits of b2 must
|
||||
* be zero.
|
||||
*/
|
||||
assign b2[WID2-1:WID1] = {(WID2-WID1){b1[WID1-1]}};
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,137 @@
|
|||
/* (c) Peter McGoron 2022 v0.1
|
||||
* 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 sck,
|
||||
input ss_L,
|
||||
`ifndef SPI_SLAVE_NO_READ
|
||||
output reg [WID-1:0] from_master,
|
||||
input reg 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
|
||||
);
|
||||
|
||||
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] <= 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) begin
|
||||
err <= ready_at_start;
|
||||
end else begin
|
||||
bit_counter <= bit_counter + 1;
|
||||
end
|
||||
endtask
|
||||
|
||||
always @ (posedge clk) 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
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,3 @@
|
|||
`define SPI_SLAVE_NO_WRITE
|
||||
/* verilator lint_off DECLFILENAME */
|
||||
`include "spi_slave.v"
|
Loading…
Reference in New Issue