Всем привет! Я начинающий разработчик, знакомых в этой области пока очень мало да и неудобно людей особо дёргать : ) Поэтому решил создать топик, чтобы те, у кого есть время, желание и соответствующая компетенция, могли помочь мне и сделать ревью кода. Заранее благодарю всех откликнувшихся.

Задача следующая: нужно считать временные отрезки между событиями и передавать значения по 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


Далее сам автомат 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


Модуль, организующий совместный сигнал старта и разрешающий остановку таймерам после выдержки времени 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


Модуль 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

Поскольку данные выставляются на шину с частотой 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


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


Файл с констрейнами 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