diff options
author | Pavel V. Shatov (Meister) <meisterpaul1@yandex.ru> | 2018-07-05 21:44:28 +0300 |
---|---|---|
committer | Pavel V. Shatov (Meister) <meisterpaul1@yandex.ru> | 2018-07-05 21:44:28 +0300 |
commit | 1ab18ea4258a4e614422d66905776d8d8302b841 (patch) | |
tree | c2c52cf31121c8203dd834c896175175639150f8 | |
parent | 0bcccfe4c8a618f2826f70a4d0561808239052bf (diff) |
FMC arbiter overhaul.
-rw-r--r-- | src/rtl/fmc_arbiter.v | 359 |
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 |