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
|
/************ Introduction to PI Controllers
|
||||||
* The continuous form of a PI loop is
|
* The continuous form of a PI loop is
|
||||||
*
|
*
|
||||||
|
@ -129,16 +131,17 @@ module control_loop
|
||||||
|
|
||||||
output adc_sck,
|
output adc_sck,
|
||||||
input adc_in,
|
input adc_in,
|
||||||
output adc_conv,
|
output adc_conv, // active high
|
||||||
|
|
||||||
output dac_sck,
|
output dac_sck,
|
||||||
output dac_ss,
|
output dac_ss, // active high
|
||||||
output dac_out,
|
output dac_out,
|
||||||
|
|
||||||
/* Informational output.
|
/* Informational output.
|
||||||
* These registers are also used for storing information while
|
* These registers are also used for storing information while
|
||||||
* the loop is running.
|
* the loop is running.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
output signed [ERR_WID-1:0] err_cur,
|
output signed [ERR_WID-1:0] err_cur,
|
||||||
output signed [CONSTS_WID-1:0] adj,
|
output signed [CONSTS_WID-1:0] adj,
|
||||||
|
|
||||||
|
@ -375,20 +378,24 @@ always @ (posedge clk) begin
|
||||||
state <= WAIT_ON_ADC;
|
state <= WAIT_ON_ADC;
|
||||||
timer <= 0;
|
timer <= 0;
|
||||||
adc_arm <= 1;
|
adc_arm <= 1;
|
||||||
|
adc_conv <= 1;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
WAIT_ON_ADC: if (adc_finished) begin
|
WAIT_ON_ADC: if (adc_finished) begin
|
||||||
adc_arm <= 0;
|
adc_arm <= 0;
|
||||||
|
adc_conv <= 0;
|
||||||
arm_mul <= 1;
|
arm_mul <= 1;
|
||||||
state <= WAIT_ON_MUL;
|
state <= WAIT_ON_MUL;
|
||||||
end
|
end
|
||||||
WAIT_ON_MUL: if (mul_finished) begin
|
WAIT_ON_MUL: if (mul_finished) begin
|
||||||
arm_mul <= 0;
|
arm_mul <= 0;
|
||||||
dac_arm <= 1;
|
dac_arm <= 1;
|
||||||
|
dac_ss <= 1;
|
||||||
state <= WAIT_ON_DAC;
|
state <= WAIT_ON_DAC;
|
||||||
end
|
end
|
||||||
WAIT_ON_DAC: if (dac_finished) begin
|
WAIT_ON_DAC: if (dac_finished) begin
|
||||||
state <= WAIT_ON_ARM;
|
state <= WAIT_ON_ARM;
|
||||||
|
dac_ss <= 0;
|
||||||
dac_arm <= 0;
|
dac_arm <= 0;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,11 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <verilated.h>
|
#include <verilated.h>
|
||||||
#include "Vcontrol_loop.h"
|
#include "Vcontrol_loop.h"
|
||||||
|
@ -19,21 +22,190 @@ Vcontrol_loop *mod;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class Transfer {
|
class Transfer {
|
||||||
std::random_device rd;
|
std::default_random_engine generator;
|
||||||
std::normal_distribution dist;
|
std::normal_distribution dist;
|
||||||
double scale;
|
double scale;
|
||||||
|
|
||||||
double sample() {return scale*dist(rd);}
|
double sample() {return scale*dist(rd);}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Transfer(double scale, double mean, double dev, double m, double b)
|
Transfer(double scale, double mean, double dev, double m, double b, int seed)
|
||||||
: scale{scale}, rd{}, dist{mean,dev} {}
|
: scale{scale}, dist{mean,dev}, generator{} {
|
||||||
|
if (seed < 0) {
|
||||||
|
std::random_device rd;
|
||||||
|
generator.seed(rd());
|
||||||
|
} else {
|
||||||
|
generator.seed(seed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
double val(double x) {
|
double val(double x) {
|
||||||
return m*x + b + sample();
|
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 DAC_DATA_WID = 20,
|
||||||
parameter CONSTS_WID = 48,
|
parameter CONSTS_WID = 48,
|
||||||
parameter DELAY_WID = 16
|
parameter DELAY_WID = 16
|
||||||
(
|
)(
|
||||||
input clk,
|
input clk,
|
||||||
input signed [ADC_WID-1:0] read_data,
|
input arm,
|
||||||
output signed [DAC_WID-1:0] write_data,
|
|
||||||
|
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 [ADC_WID-1:0] setpt,
|
||||||
input signed [CONSTS_WID-1:0] alpha,
|
input signed [CONSTS_WID-1:0] alpha,
|
||||||
input signed [CONSTS_WID-1:0] cl_p,
|
input signed [CONSTS_WID-1:0] cl_p,
|
||||||
|
@ -22,21 +24,79 @@ module top
|
||||||
|
|
||||||
wire adc_sck;
|
wire adc_sck;
|
||||||
wire adc_ss;
|
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 #(
|
spi_slave_no_write #(
|
||||||
.WID(ADC_WID),
|
.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 #(
|
control_loop #(
|
||||||
.ADC_WID(ADC_WID),
|
.ADC_WID(ADC_WID),
|
||||||
.DAC_WID(DAC_WID),
|
.DAC_WID(DAC_WID),
|
||||||
.DAC_DATA_WID(DAC_DATA_WID),
|
.DAC_DATA_WID(DAC_DATA_WID),
|
||||||
.CONSTS_WID(CONSTS_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 (
|
) 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
|
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