aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavel V. Shatov (Meister) <meisterpaul1@yandex.ru>2018-07-05 21:44:28 +0300
committerPavel V. Shatov (Meister) <meisterpaul1@yandex.ru>2018-07-05 21:44:28 +0300
commit1ab18ea4258a4e614422d66905776d8d8302b841 (patch)
treec2c52cf31121c8203dd834c896175175639150f8
parent0bcccfe4c8a618f2826f70a4d0561808239052bf (diff)
FMC arbiter overhaul.
-rw-r--r--src/rtl/fmc_arbiter.v359
1 files changed, 171 insertions, 188 deletions
diff --git a/src/rtl/fmc_arbiter.v b/src/rtl/fmc_arbiter.v
index d6fb950..037d640 100644
--- a/src/rtl/fmc_arbiter.v
+++ b/src/rtl/fmc_arbiter.v
@@ -7,7 +7,7 @@
//
//
// Author: Pavel Shatov
-// Copyright (c) 2015, NORDUnet A/S All rights reserved.
+// Copyright (c) 2015, 2018 NORDUnet A/S All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
@@ -40,7 +40,6 @@
module fmc_arbiter
(
// fmc bus
- fmc_clk,
fmc_a, fmc_d,
fmc_ne1, fmc_nl, fmc_nwe, fmc_noe, fmc_nwait,
@@ -65,7 +64,6 @@ module fmc_arbiter
//
// Ports
//
- input wire fmc_clk;
input wire [NUM_ADDR_BITS-1:0] fmc_a;
inout wire [ 31:0] fmc_d;
input wire fmc_ne1;
@@ -89,8 +87,8 @@ module fmc_arbiter
/* PHY is needed to control bi-directional data bus. */
- wire [31: 0] d_ro; // value read from pins (receiver output)
- reg [31: 0] d_di; // value drives onto pins (driver input)
+ wire [31: 0] fmc_d_ro; // value read from pins (receiver output)
+ wire [31: 0] fmc_d_di; // value drives onto pins (driver input)
fmc_d_phy #
(
@@ -99,192 +97,177 @@ module fmc_arbiter
d_phy
(
.buf_io(fmc_d), // <-- connect directly to top-level bi-dir port
- .buf_di(d_di),
- .buf_ro(d_ro),
+ .buf_di(fmc_d_di),
+ .buf_ro(fmc_d_ro),
.buf_t(fmc_noe) // <-- bus direction is controlled by STM32
);
-
- //
- // FSM
- //
- localparam FMC_FSM_STATE_INIT = 5'b0_0_000; // arbiter is idle
-
- localparam FMC_FSM_STATE_WRITE_START = 5'b1_1_000; // got address to write at
- localparam FMC_FSM_STATE_WRITE_LATENCY_1 = 5'b1_1_001; // dummy state to compensate STM32's latency
- localparam FMC_FSM_STATE_WRITE_LATENCY_2 = 5'b1_1_010; // dummy state to compensate STM32's latency
- localparam FMC_FSM_STATE_WRITE_LATENCY_3 = 5'b1_1_011; // dummy state to compensate STM32's latency
- localparam FMC_FSM_STATE_WRITE_DATABEAT = 5'b1_1_100; // got data to write
- localparam FMC_FSM_STATE_WRITE_WAIT = 5'b1_1_101; // request to user-side logic sent
- localparam FMC_FSM_STATE_WRITE_DONE = 5'b1_1_111; // user-side logic acknowledged transaction
-
- localparam FMC_FSM_STATE_READ_START = 5'b1_0_000; // got address to read from
- localparam FMC_FSM_STATE_READ_LATENCY_1 = 5'b1_0_001; // dummy state to compensate STM32's latency
- localparam FMC_FSM_STATE_READ_LATENCY_2 = 5'b1_0_010; // dummy state to compensate STM32's latency
- localparam FMC_FSM_STATE_READ_LATENCY_3 = 5'b1_0_011; // dummy state to compensate STM32's latency
- localparam FMC_FSM_STATE_READ_WAIT = 5'b1_0_101; // request to user-side logic sent
- localparam FMC_FSM_STATE_READ_READY = 5'b1_0_110; // got acknowledge from user logic
- localparam FMC_FSM_STATE_READ_DATABEAT = 5'b1_0_100; // returned data to master
- localparam FMC_FSM_STATE_READ_DONE = 5'b1_0_111; // transaction complete
-
- reg [ 4:0] fmc_fsm_state = FMC_FSM_STATE_INIT; // fsm state
- reg [NUM_ADDR_BITS-1:0] fmc_addr_latch = {NUM_ADDR_BITS{1'bX}}; // transaction address
- reg [ 31:0] fmc_data_latch = {32{1'bX}}; // write data latch
-
- /* These flags are used to wake up from INIT state. */
- wire fmc_write_start_flag = (fmc_ne1 == 1'b0) && (fmc_nwe == 1'b0) && (fmc_nl == 1'b0);
- wire fmc_read_start_flag = (fmc_ne1 == 1'b0) && (fmc_nwe == 1'b1) && (fmc_nl == 1'b0);
-
- /* These are transaction response flag and data from user-side logic. */
- wire fmc_user_ack;
- wire [31: 0] fmc_user_data;
-
- //
- // FSM Transition Logic
- //
- always @(posedge fmc_clk)
- //
- case (fmc_fsm_state)
- //
- // INIT -> WRITE, INIT -> READ
- //
- FMC_FSM_STATE_INIT: begin
- //
- if (fmc_write_start_flag) fmc_fsm_state <= FMC_FSM_STATE_WRITE_START;
- if (fmc_read_start_flag) fmc_fsm_state <= FMC_FSM_STATE_READ_START;
- //
- end
- //
- // WRITE
- //
- FMC_FSM_STATE_WRITE_START: fmc_fsm_state <= FMC_FSM_STATE_WRITE_LATENCY_1;
- FMC_FSM_STATE_WRITE_LATENCY_1: fmc_fsm_state <= FMC_FSM_STATE_WRITE_LATENCY_2;
- FMC_FSM_STATE_WRITE_LATENCY_2: fmc_fsm_state <= FMC_FSM_STATE_WRITE_LATENCY_3;
- FMC_FSM_STATE_WRITE_LATENCY_3: fmc_fsm_state <= FMC_FSM_STATE_WRITE_DATABEAT;
- FMC_FSM_STATE_WRITE_DATABEAT: fmc_fsm_state <= FMC_FSM_STATE_WRITE_WAIT;
- FMC_FSM_STATE_WRITE_WAIT: if (fmc_user_ack) fmc_fsm_state <= FMC_FSM_STATE_WRITE_DONE;
- FMC_FSM_STATE_WRITE_DONE: fmc_fsm_state <= FMC_FSM_STATE_INIT;
- //
- // READ
- //
- FMC_FSM_STATE_READ_START: fmc_fsm_state <= FMC_FSM_STATE_READ_LATENCY_1;
- FMC_FSM_STATE_READ_LATENCY_1: fmc_fsm_state <= FMC_FSM_STATE_READ_LATENCY_2;
- FMC_FSM_STATE_READ_LATENCY_2: fmc_fsm_state <= FMC_FSM_STATE_READ_LATENCY_3;
- FMC_FSM_STATE_READ_LATENCY_3: fmc_fsm_state <= FMC_FSM_STATE_READ_WAIT;
- FMC_FSM_STATE_READ_WAIT: if (fmc_user_ack) fmc_fsm_state <= FMC_FSM_STATE_READ_READY;
- FMC_FSM_STATE_READ_READY: fmc_fsm_state <= FMC_FSM_STATE_READ_DATABEAT;
- FMC_FSM_STATE_READ_DATABEAT: fmc_fsm_state <= FMC_FSM_STATE_READ_DONE;
- FMC_FSM_STATE_READ_DONE: fmc_fsm_state <= FMC_FSM_STATE_INIT;
- //
- default: fmc_fsm_state <= FMC_FSM_STATE_INIT;
- //
- endcase
-
-
- //
- // Address Latch
- //
- always @(posedge fmc_clk)
- //
- if ((fmc_fsm_state == FMC_FSM_STATE_INIT) && (fmc_write_start_flag || fmc_read_start_flag))
- //
- fmc_addr_latch <= fmc_a;
-
-
- //
- // Additional Write Logic (Data Latch)
- //
- always @(posedge fmc_clk)
- //
- if (fmc_fsm_state == FMC_FSM_STATE_WRITE_LATENCY_3)
- //
- fmc_data_latch <= d_ro;
-
-
- //
- // Additional Read Logic (Read Latch)
- //
-
- /* Note that this register is updated on the falling edge of FMC_CLK, because
- * STM32 samples bi-directional data bus on the rising edge.
- */
-
- always @(negedge fmc_clk)
- //
- if (fmc_fsm_state == FMC_FSM_STATE_READ_DATABEAT)
- //
- d_di <= fmc_user_data;
-
-
-
- //
- // Wait Logic
- //
- reg fmc_wait_reg = 1'b0;
-
- always @(posedge fmc_clk)
- //
- begin
- //
- if ( (fmc_fsm_state == FMC_FSM_STATE_WRITE_START) ||
- (fmc_fsm_state == FMC_FSM_STATE_READ_START) )
- fmc_wait_reg <= 1'b1; // start waiting for read/write to complete
- /*
- if ( (fmc_fsm_state == FMC_FSM_STATE_WRITE_DONE) ||
- (fmc_fsm_state == FMC_FSM_STATE_READ_READY) )
- fmc_wait_reg <= 1'b0;
- */
- if (fmc_fsm_state == FMC_FSM_STATE_INIT)
- fmc_wait_reg <= 1'b0; // fsm is idle, no need to wait any more
- //
- end
-
- assign fmc_nwait = ~fmc_wait_reg;
-
-
- /* These flags are used to generate 1-cycle pulses to trigger CDC
- * transaction. Note that FSM goes from WRITE_DATABEAT to WRITE_WAIT and from
- * READ_LATENCY_3 to READ_WAIT unconditionally, so these flags will always be
- * active for 1 cycle only, which is exactly what we need.
- */
-
- wire arbiter_write_req_pulse = (fmc_fsm_state == FMC_FSM_STATE_WRITE_DATABEAT) ? 1'b1 : 1'b0;
- wire arbiter_read_req_pulse = (fmc_fsm_state == FMC_FSM_STATE_READ_LATENCY_3) ? 1'b1 : 1'b0;
-
- //
- // CDC Block
- //
-
- /* This block is used to transfer request data from FMC_CLK clock domain to
- * SYS_CLK clock domain and then transfer acknowledge from SYS_CLK to FMC_CLK
- * clock domain in return. Af first 1+1+22+32 = 56 bits are transfered,
- * these are: write flag, read flag, address, write data. During read transaction
- * some bogus write data is passed, which is not used later anyway.
- * During read requests 32 bits of data are returned, during write requests
- * 32 bits of bogus data are returned, that are never used later.
- */
-
- fmc_arbiter_cdc #
- (
- .NUM_ADDR_BITS(NUM_ADDR_BITS)
- )
- fmc_cdc
- (
- .fmc_clk(fmc_clk),
-
- .fmc_req(arbiter_write_req_pulse | arbiter_read_req_pulse),
- .fmc_ack(fmc_user_ack),
-
- .fmc_din({arbiter_write_req_pulse, arbiter_read_req_pulse, fmc_addr_latch, fmc_data_latch}),
- .fmc_dout(fmc_user_data),
-
- .sys_clk(sys_clk),
- .sys_addr(sys_addr),
- .sys_wren(sys_wr_en),
- .sys_data_out(sys_data_out),
- .sys_rden(sys_rd_en),
- .sys_data_in(sys_data_in)
- );
+ //
+ // Two-Stage Synchronizer
+ //
+ (* SHREG_EXTRACT="NO" *) (* IOB="FALSE" *) reg [23: 0] fmc_a_ff1;
+ (* SHREG_EXTRACT="NO" *) reg [23: 0] fmc_a_ff2;
+
+ (* SHREG_EXTRACT="NO" *) (* IOB="TRUE" *) reg [31: 0] fmc_d_ro_ff1;
+ (* SHREG_EXTRACT="NO" *) reg [31: 0] fmc_d_ro_ff2;
+
+ (* SHREG_EXTRACT="NO" *) (* IOB="FALSE" *) reg fmc_ne1_ff1;
+ (* SHREG_EXTRACT="NO" *) reg fmc_ne1_ff2;
+
+ (* SHREG_EXTRACT="NO" *) (* IOB="FALSE" *) reg fmc_nwe_ff1;
+ (* SHREG_EXTRACT="NO" *) reg fmc_nwe_ff2;
+
+ (* SHREG_EXTRACT="NO" *) (* IOB="FALSE" *) reg fmc_nl_ff1;
+ (* SHREG_EXTRACT="NO" *) reg fmc_nl_ff2;
+
+ wire [23: 0] fmc_a_sync = fmc_a_ff2;
+ wire [31: 0] fmc_d_ro_sync = fmc_d_ro_ff2;
+ wire fmc_ne1_sync = fmc_ne1_ff2;
+ wire fmc_nwe_sync = fmc_nwe_ff2;
+ wire fmc_nl_sync = fmc_nl_ff2;
+
+ always @(posedge sys_clk) begin
+ fmc_a_ff1 <= fmc_a;
+ fmc_a_ff2 <= fmc_a_ff1;
+
+ fmc_d_ro_ff1 <= fmc_d_ro;
+ fmc_d_ro_ff2 <= fmc_d_ro_ff1;
+
+ fmc_ne1_ff1 <= fmc_ne1;
+ fmc_ne1_ff2 <= fmc_ne1_ff1;
+
+ fmc_nwe_ff1 <= fmc_nwe;
+ fmc_nwe_ff2 <= fmc_nwe_ff1;
+
+ fmc_nl_ff1 <= fmc_nl;
+ fmc_nl_ff2 <= fmc_nl_ff1;
+ end
+
+
+ //
+ // FSM
+ //
+ localparam FSM_STATE_IDLE = 4'd0;
+
+ localparam FSM_STATE_WRITE_START = 4'd1;
+ localparam FSM_STATE_WRITE_LATENCY_1 = 4'd2;
+ localparam FSM_STATE_WRITE_LATENCY_2 = 4'd3;
+ localparam FSM_STATE_WRITE_LATENCY_3 = 4'd4;
+ localparam FSM_STATE_WRITE_LATENCY_4 = 4'd5;
+ localparam FSM_STATE_WRITE_STOP = 4'd6;
+
+ localparam FSM_STATE_READ_START = 4'd7;
+ localparam FSM_STATE_READ_LATENCY_1 = 4'd8;
+ localparam FSM_STATE_READ_STOP = 4'd9;
+
+ reg [ 3: 0] fsm_state = FSM_STATE_IDLE;
+ reg [ 3: 0] fsm_state_next;
+
+ always @(posedge sys_clk)
+ //
+ fsm_state <= fsm_state_next;
+
+
+ //
+ // FSM Transition Logic
+ //
+ always @*
+ //
+ if (fmc_ne1_sync) fsm_state_next = FSM_STATE_IDLE;
+ else case (fsm_state)
+ FSM_STATE_IDLE: fsm_state_next = !fmc_nwe_sync ? FSM_STATE_WRITE_START : FSM_STATE_READ_START;
+ FSM_STATE_WRITE_START: fsm_state_next = FSM_STATE_WRITE_LATENCY_1;
+ FSM_STATE_WRITE_LATENCY_1: fsm_state_next = FSM_STATE_WRITE_LATENCY_2;
+ FSM_STATE_WRITE_LATENCY_2: fsm_state_next = FSM_STATE_WRITE_LATENCY_3;
+ FSM_STATE_WRITE_LATENCY_3: fsm_state_next = FSM_STATE_WRITE_LATENCY_4;
+ FSM_STATE_WRITE_LATENCY_4,
+ FSM_STATE_WRITE_STOP: fsm_state_next = FSM_STATE_WRITE_STOP;
+ FSM_STATE_READ_START: fsm_state_next = FSM_STATE_READ_LATENCY_1;
+ FSM_STATE_READ_LATENCY_1,
+ FSM_STATE_READ_STOP: fsm_state_next = FSM_STATE_READ_STOP;
+ default: fsm_state_next = FSM_STATE_IDLE;
+ endcase
+
+
+ //
+ // Output Data Latch
+ //
+ (* IOB="TRUE" *)
+ reg [31:0] sys_data_in_latch;
+
+ assign fmc_d_di = sys_data_in_latch;
+
+
+ //
+ // Address Latch
+ //
+ always @(posedge sys_clk)
+ //
+ if (!fmc_ne1_sync && !fmc_nl_sync)
+ //
+ sys_addr_reg <= fmc_a_sync;
+
+
+ //
+ // System Interface
+ //
+ reg [NUM_ADDR_BITS-1:0] sys_addr_reg;
+ reg sys_wr_en_reg = 1'b0;
+ reg [ 31:0] sys_data_out_reg;
+ reg sys_rd_en_reg = 1'b0;
+
+ assign sys_addr = sys_addr_reg;
+ assign sys_wr_en = sys_wr_en_reg;
+ assign sys_data_out = sys_data_out_reg;
+ assign sys_rd_en = sys_rd_en_reg;
+
+
+ //
+ // Write Enable Logic
+ //
+ always @(posedge sys_clk)
+ //
+ case (fsm_state)
+ FSM_STATE_WRITE_LATENCY_4: sys_wr_en_reg <= 1'b1;
+ default: sys_wr_en_reg <= 1'b0;
+ endcase
+
+
+ //
+ // Read Enable Logic
+ //
+ always @(posedge sys_clk)
+ //
+ case (fsm_state_next)
+ FSM_STATE_READ_START: sys_rd_en_reg <= 1'b1;
+ default: sys_rd_en_reg <= 1'b0;
+ endcase
+
+
+ //
+ // Output Data Latch
+ //
+ always @(posedge sys_clk)
+ //
+ case (fsm_state)
+ FSM_STATE_READ_LATENCY_1: sys_data_in_latch <= sys_data_in;
+ endcase
+
+
+ //
+ // Input Data Latch
+ //
+ always @(posedge sys_clk)
+ //
+ case (fsm_state)
+ FSM_STATE_WRITE_LATENCY_4: sys_data_out_reg <= fmc_d_ro_sync;
+ endcase
+
+
+ //
+ // Unused NWAIT tieoff
+ //
+ assign fmc_nwait = 1'b0;
endmodule