Задача следующая: нужно считать временные отрезки между событиями и передавать значения по SPI.
Есть 32 входа (ширина 1 бит) и 32 таймера (ширина 16 бит). Таймеры изначально сброшены и остановлены. Когда на 4 и более входах фиксируется лог. 1, таймеры одновременно начинают отсчёт, ожидая сигнала остановки. Сигнал остановки -- это лог. 1 на соответствующем входе. При фиксации сигнала остановки таймер останавливает счёт. Если сигнал остановки не пришёл, таймер останавливается по достижению максимального значения (во временной области это 3 мс). По окончанию счёта всех таймеров выставляется флаг готовности, после чего внешнее устройство -- SPI-master -- готово считывать данные (частота 8 МГц). По окончанию считывания таймеры снова ждут сигнала старта.
Я решил запилить конечный автомат на 4 состояния.
Тайпдеф запихнул в пакет types.sv (расширение оставил sv, т.к. в противном случае квартус не подсвечивает ключевые слова)):
CODE
`ifndef TYPES_SV
`define TYPES_SV
package types;
typedef enum logic [ 1 : 0 ] {
IDLE = 2'd0,
COUNT = 2'd1,
RECEIVE_WAITING = 2'd2,
RECEIVING = 2'd3
} state;
endpackage
import types::*;
`endif // !TYPES_SV
`define TYPES_SV
package types;
typedef enum logic [ 1 : 0 ] {
IDLE = 2'd0,
COUNT = 2'd1,
RECEIVE_WAITING = 2'd2,
RECEIVING = 2'd3
} state;
endpackage
import types::*;
`endif // !TYPES_SV
Далее сам автомат FSM.sv:
CODE
`include "types.sv"
module FSM(
input logic n_rst_i,
input logic clk_i,
input logic en_i,
input logic start_i,
input logic stop_i,
input logic n_CS_i,
output state state_o,
output logic data_ready_o
);
state next_state;
assign data_ready_o = ( state_o == RECEIVE_WAITING );
always_ff @( posedge clk_i or negedge n_rst_i ) begin
if ( !n_rst_i ) begin
state_o <= IDLE;
end else begin
if ( en_i ) begin
state_o <= next_state;
end
end
end
always_comb begin
if ( en_i ) begin
case ( state_o )
IDLE: begin
if ( start_i ) begin
next_state = COUNT;
end else begin
next_state = IDLE;
end
end
COUNT: begin
if ( stop_i ) begin
next_state = RECEIVE_WAITING;
end else begin
next_state = COUNT;
end
end
RECEIVE_WAITING: begin
if ( !n_CS_i ) begin
next_state = RECEIVING;
end else begin
next_state = RECEIVE_WAITING;
end
end
RECEIVING: begin
if ( n_CS_i ) begin
next_state = IDLE;
end else begin
next_state = RECEIVING;
end
end
endcase
end else begin
next_state = state_o;
end
end
endmodule
Таймер timer.sv:
CODE
module timer #( parameter WIDTH = 16, parameter STOP_VALUE = 16'hFFFF ) (
input logic n_rst_i,
input logic clk_i,
input logic en_i,
input logic start_i,
input logic stop_i,
output logic stopped_o,
output logic [ WIDTH - 1 : 0 ] value_o
);
always_ff @( negedge n_rst_i or posedge clk_i ) begin
if ( !n_rst_i ) begin
value_o <= '0;
stopped_o <= '1;
end else begin
if ( en_i ) begin
if ( start_i && stopped_o ) begin
value_o <= '0;
stopped_o <= '0;
end
if ( stop_i ) begin
stopped_o <= '1;
end
if ( !stopped_o ) begin
if ( value_o < STOP_VALUE ) begin
value_o <= value_o + 1'b1;
end else begin
stopped_o <= '1;
end
end
end
end
end
endmodule
input logic n_rst_i,
input logic clk_i,
input logic en_i,
input logic start_i,
input logic stop_i,
output logic stopped_o,
output logic [ WIDTH - 1 : 0 ] value_o
);
always_ff @( negedge n_rst_i or posedge clk_i ) begin
if ( !n_rst_i ) begin
value_o <= '0;
stopped_o <= '1;
end else begin
if ( en_i ) begin
if ( start_i && stopped_o ) begin
value_o <= '0;
stopped_o <= '0;
end
if ( stop_i ) begin
stopped_o <= '1;
end
if ( !stopped_o ) begin
if ( value_o < STOP_VALUE ) begin
value_o <= value_o + 1'b1;
end else begin
stopped_o <= '1;
end
end
end
end
end
endmodule
Модуль, организующий совместный сигнал старта и разрешающий остановку таймерам после выдержки времени start_stop_logic.sv:
CODE
`include "types.sv"
module start_stop_logic #(parameter N = 4 ) (
input logic n_rst_i,
input logic clk_i,
input logic en_i,
input state current_state_i,
input logic [ 31 : 0 ] signal_i,
output logic start_o,
output logic [ 15 : 0 ] stop_delay_o
);
localparam input_freq = 21_845_000;
localparam delay_time_ms = 1;
localparam delay_count = ( ( input_freq / 1000 ) * delay_time_ms ) - 1;
logic [ 5 : 0 ] simultaneous_signals;
always_comb begin
simultaneous_signals = '0;
for ( int i = 0; i < 32; ++i ) begin
simultaneous_signals = simultaneous_signals + signal_i;
end
end
always_ff @( posedge clk_i or negedge n_rst_i ) begin
if ( !n_rst_i ) begin
start_o <= '0;
stop_delay_o <= '0;
end else begin
if ( en_i ) begin
if ( current_state_i == IDLE ) begin
if ( simultaneous_signals > ( N - 1 ) ) begin
start_o <= '1;
stop_delay_o <= delay_count[15 : 0];
end
end else begin
start_o <= '0;
end
if ( current_state_i == COUNT && ( stop_delay_o != '0 ) ) begin
stop_delay_o <= stop_delay_o - 1'b1;
end
end
end
end
endmodule
module start_stop_logic #(parameter N = 4 ) (
input logic n_rst_i,
input logic clk_i,
input logic en_i,
input state current_state_i,
input logic [ 31 : 0 ] signal_i,
output logic start_o,
output logic [ 15 : 0 ] stop_delay_o
);
localparam input_freq = 21_845_000;
localparam delay_time_ms = 1;
localparam delay_count = ( ( input_freq / 1000 ) * delay_time_ms ) - 1;
logic [ 5 : 0 ] simultaneous_signals;
always_comb begin
simultaneous_signals = '0;
for ( int i = 0; i < 32; ++i ) begin
simultaneous_signals = simultaneous_signals + signal_i;
end
end
always_ff @( posedge clk_i or negedge n_rst_i ) begin
if ( !n_rst_i ) begin
start_o <= '0;
stop_delay_o <= '0;
end else begin
if ( en_i ) begin
if ( current_state_i == IDLE ) begin
if ( simultaneous_signals > ( N - 1 ) ) begin
start_o <= '1;
stop_delay_o <= delay_count[15 : 0];
end
end else begin
start_o <= '0;
end
if ( current_state_i == COUNT && ( stop_delay_o != '0 ) ) begin
stop_delay_o <= stop_delay_o - 1'b1;
end
end
end
end
endmodule
Модуль SPI-slave [i]spi_logic.sv:
CODE
`include "types.sv"
module spi_logic (
input logic n_rst_i,
input logic clk_i,
input logic en_i,
input logic n_CS_i,
input logic SCK_i,
input logic [ 511 : 0 ] data_i,
input state current_state_i,
output tri MISO_o
);
logic [ 8 : 0 ] data_counter;
logic prepare_data;
logic shift_data;
logic MISO;
assign MISO_o = ( !n_rst_i || n_CS_i || !en_i ) ? 'z : MISO;
always_ff @( posedge clk_i or negedge n_rst_i ) begin
if ( !n_rst_i ) begin
MISO <= '0;
data_counter <= '0;
prepare_data <= '0;
shift_data <= '0;
end else begin
prepare_data <= n_CS_i;
shift_data <= SCK_i;
if ( n_CS_i ) begin
if ( current_state_i == RECEIVE_WAITING ) begin
MISO <= data_i[511];
end else begin
MISO <= '1;
end
end else begin
if ( prepare_data ) begin
data_counter <= 510;
end else begin
if ( ( data_counter != '1 ) && !shift_data && SCK_i ) begin
data_counter <= data_counter - 1'b1;
if ( current_state_i == RECEIVING ) begin
MISO <= data_i[data_counter];
end else begin
MISO <= '1;
end
end
end
end
end
end
endmodule
module spi_logic (
input logic n_rst_i,
input logic clk_i,
input logic en_i,
input logic n_CS_i,
input logic SCK_i,
input logic [ 511 : 0 ] data_i,
input state current_state_i,
output tri MISO_o
);
logic [ 8 : 0 ] data_counter;
logic prepare_data;
logic shift_data;
logic MISO;
assign MISO_o = ( !n_rst_i || n_CS_i || !en_i ) ? 'z : MISO;
always_ff @( posedge clk_i or negedge n_rst_i ) begin
if ( !n_rst_i ) begin
MISO <= '0;
data_counter <= '0;
prepare_data <= '0;
shift_data <= '0;
end else begin
prepare_data <= n_CS_i;
shift_data <= SCK_i;
if ( n_CS_i ) begin
if ( current_state_i == RECEIVE_WAITING ) begin
MISO <= data_i[511];
end else begin
MISO <= '1;
end
end else begin
if ( prepare_data ) begin
data_counter <= 510;
end else begin
if ( ( data_counter != '1 ) && !shift_data && SCK_i ) begin
data_counter <= data_counter - 1'b1;
if ( current_state_i == RECEIVING ) begin
MISO <= data_i[data_counter];
end else begin
MISO <= '1;
end
end
end
end
end
end
endmodule
Поскольку данные выставляются на шину с частотой SCK, я "ловлю" фронт SCK путём сравнения текущего и предыдущего состояний этого входа. Таким же образом фиксирую сигнал чип-селекта, который может в любой момент прийти асинхронно.
Ну и сам топ, объединяющий всё это. top.sv:
CODE
`include "types.sv"
module top #( parameter START = 4, TIMERS = 32, parameter WIDTH = 16 ) (
n_rst_i,
clk_i,
chip_enable_i,
signal_i,
//MOSI_i,
SCK_i,
n_CS_i,
MISO_o,
data_ready_o
);
/* ================= PIN DESCRIPTION =================*/
input logic n_rst_i /* synthesis chip_pin = "8" */
/* synthesis altera_attribute = "-name IO_STANDARD \"3.3-V LVTTL\"" */;
input logic clk_i /* synthesis chip_pin = "27" */
/* synthesis altera_attribute = "-name IO_STANDARD \"3.3-V LVTTL\"" */;
input logic chip_enable_i /* synthesis chip_pin = "14" */
/* synthesis altera_attribute = "-name IO_STANDARD \"3.3-V LVTTL\"" */;
input logic [ 31 : 0 ] signal_i /* synthesis chip_pin = "65, 66, 69, 70, 74, 75, 76, 77, 79, 81, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 96, 98, 99, 100, 101, 102, 105, 106, 110, 111, 113, 114" */
/* synthesis altera_attribute = "-name IO_STANDARD \"3.3-V LVTTL\"" */;
//input logic MOSI_i synthesis chip_pin = "11" synthesis altera_attribute = "-name IO_STANDARD \"3.3-V LVTTL\"";
input logic SCK_i /* synthesis chip_pin = "12" */
/* synthesis altera_attribute = "-name IO_STANDARD \"3.3-V LVTTL\"" */;
input logic n_CS_i /* synthesis chip_pin = "13" */
/* synthesis altera_attribute = "-name IO_STANDARD \"3.3-V LVTTL\"" */;
output tri MISO_o /* synthesis chip_pin = "10" */
/* synthesis altera_attribute = "-name IO_STANDARD \"3.3-V LVTTL\"" */;
output logic data_ready_o /* synthesis chip_pin = "15" */
/* synthesis altera_attribute = "-name IO_STANDARD \"3.3-V LVTTL\"" */;
/* ================= INTERNAL SIGNALS =================
================= AND REGISTERS ================= */
logic clk;
logic pll_locked;
logic start;
logic [ 15 : 0 ] stop_delay;
logic [ 31 : 0 ] stop;
state current_state;
logic [ 511 : 0 ] data;
/* ================= INTERNAL MODULES ================= */
pll PLL ( .areset( !n_rst_i ),
.inclk0( clk_i ),
.c0 ( clk ),
.locked( pll_locked )
);
FSM state_choose( .*, .clk_i ( clk ),
.en_i ( chip_enable_i && pll_locked ),
.start_i( start ),
.stop_i ( stop == '1 && ( stop_delay < 10 ) ),
.state_o( current_state )
);
start_stop_logic #( START ) start_stop ( .*, .clk_i ( clk ),
.en_i ( chip_enable_i && pll_locked ),
.current_state_i( current_state ),
.start_o ( start ),
.stop_delay_o ( stop_delay )
);
spi_logic spi ( .*, .clk_i ( clk ),
.en_i ( chip_enable_i && pll_locked ),
.data_i ( data ),
.current_state_i( current_state )
);
genvar i;
generate
for ( i = 0; i < 32; ++i ) begin : TIMER
timer #( WIDTH, 2**WIDTH - 1 ) tim ( .*, .clk_i ( clk ),
.en_i ( chip_enable_i && pll_locked ),
.start_i ( start && ( current_state == IDLE ) ),
.stop_i ( signal_i && ( current_state == COUNT ) && ( stop_delay < 10 ) ),
.stopped_o( stop[i] ),
.value_o ( data[ ( ( TIMERS - i ) * WIDTH - 1 ) : ( ( TIMERS - i - 1 ) * WIDTH ) ] )
);
end : TIMER
endgenerate
endmodule
module top #( parameter START = 4, TIMERS = 32, parameter WIDTH = 16 ) (
n_rst_i,
clk_i,
chip_enable_i,
signal_i,
//MOSI_i,
SCK_i,
n_CS_i,
MISO_o,
data_ready_o
);
/* ================= PIN DESCRIPTION =================*/
input logic n_rst_i /* synthesis chip_pin = "8" */
/* synthesis altera_attribute = "-name IO_STANDARD \"3.3-V LVTTL\"" */;
input logic clk_i /* synthesis chip_pin = "27" */
/* synthesis altera_attribute = "-name IO_STANDARD \"3.3-V LVTTL\"" */;
input logic chip_enable_i /* synthesis chip_pin = "14" */
/* synthesis altera_attribute = "-name IO_STANDARD \"3.3-V LVTTL\"" */;
input logic [ 31 : 0 ] signal_i /* synthesis chip_pin = "65, 66, 69, 70, 74, 75, 76, 77, 79, 81, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 96, 98, 99, 100, 101, 102, 105, 106, 110, 111, 113, 114" */
/* synthesis altera_attribute = "-name IO_STANDARD \"3.3-V LVTTL\"" */;
//input logic MOSI_i synthesis chip_pin = "11" synthesis altera_attribute = "-name IO_STANDARD \"3.3-V LVTTL\"";
input logic SCK_i /* synthesis chip_pin = "12" */
/* synthesis altera_attribute = "-name IO_STANDARD \"3.3-V LVTTL\"" */;
input logic n_CS_i /* synthesis chip_pin = "13" */
/* synthesis altera_attribute = "-name IO_STANDARD \"3.3-V LVTTL\"" */;
output tri MISO_o /* synthesis chip_pin = "10" */
/* synthesis altera_attribute = "-name IO_STANDARD \"3.3-V LVTTL\"" */;
output logic data_ready_o /* synthesis chip_pin = "15" */
/* synthesis altera_attribute = "-name IO_STANDARD \"3.3-V LVTTL\"" */;
/* ================= INTERNAL SIGNALS =================
================= AND REGISTERS ================= */
logic clk;
logic pll_locked;
logic start;
logic [ 15 : 0 ] stop_delay;
logic [ 31 : 0 ] stop;
state current_state;
logic [ 511 : 0 ] data;
/* ================= INTERNAL MODULES ================= */
pll PLL ( .areset( !n_rst_i ),
.inclk0( clk_i ),
.c0 ( clk ),
.locked( pll_locked )
);
FSM state_choose( .*, .clk_i ( clk ),
.en_i ( chip_enable_i && pll_locked ),
.start_i( start ),
.stop_i ( stop == '1 && ( stop_delay < 10 ) ),
.state_o( current_state )
);
start_stop_logic #( START ) start_stop ( .*, .clk_i ( clk ),
.en_i ( chip_enable_i && pll_locked ),
.current_state_i( current_state ),
.start_o ( start ),
.stop_delay_o ( stop_delay )
);
spi_logic spi ( .*, .clk_i ( clk ),
.en_i ( chip_enable_i && pll_locked ),
.data_i ( data ),
.current_state_i( current_state )
);
genvar i;
generate
for ( i = 0; i < 32; ++i ) begin : TIMER
timer #( WIDTH, 2**WIDTH - 1 ) tim ( .*, .clk_i ( clk ),
.en_i ( chip_enable_i && pll_locked ),
.start_i ( start && ( current_state == IDLE ) ),
.stop_i ( signal_i && ( current_state == COUNT ) && ( stop_delay < 10 ) ),
.stopped_o( stop[i] ),
.value_o ( data[ ( ( TIMERS - i ) * WIDTH - 1 ) : ( ( TIMERS - i - 1 ) * WIDTH ) ] )
);
end : TIMER
endgenerate
endmodule
Testbench [i]tb.sv:
CODE
`include "types.sv"
`timescale 1 ps / 1 ps
module tb;
logic n_rst_i;
logic clk_i;
logic chip_enable_i;
logic [ 31 : 0 ] signal_i;
//logic MOSI_i;
logic SCK_i;
logic n_CS_i;
tri MISO_o;
logic data_ready_o;
state prev_state;
top DUT( .*);
always begin
#10ns;
clk_i = !clk_i;
end
always begin
#62.5ns;
if ( !n_CS_i ) begin
SCK_i = !SCK_i;
end else begin
SCK_i = 0;
end
end
initial begin
n_rst_i = '0;
clk_i = '0;
chip_enable_i = '0;
signal_i = '0;
//MOSI_i = '0;
SCK_i = '0;
n_CS_i = '1;
#70ns;
n_rst_i = '1;
#100ns;
chip_enable_i = '1;
end
always begin
@( posedge clk_i )
@( posedge clk_i )
@( posedge clk_i )
@( posedge clk_i )
case ( DUT.current_state )
IDLE: begin
signal_i <= '1;
prev_state <= IDLE;
end
COUNT: begin
if ( prev_state == IDLE || DUT.stop_delay != '0) begin
signal_i <= 1;
prev_state <= COUNT;
end else begin
signal_i <= signal_i << 1;
end
end
endcase
end
always @ ( posedge data_ready_o ) begin
#125ns;
n_CS_i = '0;
#65000ns;
n_CS_i = '1;
end
initial begin
#8ms;
$stop;
end
endmodule
`timescale 1 ps / 1 ps
module tb;
logic n_rst_i;
logic clk_i;
logic chip_enable_i;
logic [ 31 : 0 ] signal_i;
//logic MOSI_i;
logic SCK_i;
logic n_CS_i;
tri MISO_o;
logic data_ready_o;
state prev_state;
top DUT( .*);
always begin
#10ns;
clk_i = !clk_i;
end
always begin
#62.5ns;
if ( !n_CS_i ) begin
SCK_i = !SCK_i;
end else begin
SCK_i = 0;
end
end
initial begin
n_rst_i = '0;
clk_i = '0;
chip_enable_i = '0;
signal_i = '0;
//MOSI_i = '0;
SCK_i = '0;
n_CS_i = '1;
#70ns;
n_rst_i = '1;
#100ns;
chip_enable_i = '1;
end
always begin
@( posedge clk_i )
@( posedge clk_i )
@( posedge clk_i )
@( posedge clk_i )
case ( DUT.current_state )
IDLE: begin
signal_i <= '1;
prev_state <= IDLE;
end
COUNT: begin
if ( prev_state == IDLE || DUT.stop_delay != '0) begin
signal_i <= 1;
prev_state <= COUNT;
end else begin
signal_i <= signal_i << 1;
end
end
endcase
end
always @ ( posedge data_ready_o ) begin
#125ns;
n_CS_i = '0;
#65000ns;
n_CS_i = '1;
end
initial begin
#8ms;
$stop;
end
endmodule
Файл с констрейнами top.sdc:
Код
derive_clock_uncertainty
create_clock -name main_clock -period 20.000 [get_ports {clk_i}]
create_generated_clock -name pll_clock -source [get_ports {clk_i}] -divide_by 119 -multiply_by 52 -duty_cycle 50 -phase 2.65 -offset 0 [get_nets {PLL|altpll_component|auto_generated|wire_pll1_clk[0]}]
derive_pll_clocks
create_clock -name main_clock -period 20.000 [get_ports {clk_i}]
create_generated_clock -name pll_clock -source [get_ports {clk_i}] -divide_by 119 -multiply_by 52 -duty_cycle 50 -phase 2.65 -offset 0 [get_nets {PLL|altpll_component|auto_generated|wire_pll1_clk[0]}]
derive_pll_clocks