mirror of https://github.com/lnis-uofu/SOFA.git
965 lines
32 KiB
Verilog
965 lines
32 KiB
Verilog
/* -----------------------------------------------------------------------------
|
|
title : FlexFusionEngine Control
|
|
project : Jim-Bob Hardware Sensor Hub
|
|
-----------------------------------------------------------------------------
|
|
platform : Alabama test chip
|
|
standard : Verilog 2001
|
|
-----------------------------------------------------------------------------
|
|
description: Module for controlling the FlexFusionEngine
|
|
-----------------------------------------------------------------------------
|
|
copyright (c) 2013, QuickLogic Corporation
|
|
-----------------------------------------------------------------------------
|
|
History :
|
|
Date Version Author Description
|
|
2013/03/21 1.0 Jason Lew Created
|
|
2013/05/02 1.1 Jason Lew Migrated from FFEAT v21d
|
|
2013/06/14 1.2 Randy O. Corrected assignment to DataMemReadAddr's b/c it wasn't using IndexReg properly.
|
|
Corrected assignment to MemReadData's b/c it shouldn't use IndexReg.
|
|
2013/06/26 1.3 Randy O. Made the Signals bus 64 bits wide instead of 32, since it needs to be at least as wide as the one in microopdecodes.v
|
|
2013/07/01 1.4 Randy O. Cosmetic changes to improve readability.
|
|
Removed DataMem1WriteData_int & DataMem2WriteData_int since they were unused.
|
|
2013/07/08 1.5 Randy O. Added unit delays to aid in functional sim.
|
|
2014/05/21 1.6 Glen G. Added ability to read/write expanded Sensor Manager Memory
|
|
|
|
-----------------------------------------------------------------------------
|
|
Comments: This solution is specifically for implementing into the Alabama
|
|
test chip. Verification will be done using the Jim-Bob Sensor Board
|
|
------------------------------------------------------------------------------*/
|
|
|
|
`include "ulpsh_rtl_defines.v"
|
|
|
|
|
|
`timescale 1ns / 10ps
|
|
|
|
module FFE_Control ( // named RunFlexFusionEngine in C source
|
|
input ClockIn,
|
|
input Clock_x2In,
|
|
input ResetIn,
|
|
input StartIn,
|
|
output StartSMOut,
|
|
input [15:0] TimeStampIn,
|
|
input [31:0] MailboxIn,
|
|
input [3:0] SM_InterruptIn,
|
|
output [11:0] ControlMemAddressOut,
|
|
output reg ControlMemReadEnableOut,
|
|
output [9:0] SensorMemReadAddressOut, // Expanded for Rel 0 on 6/18
|
|
output SensorMemReadEnableOut,
|
|
output [9:0] SensorMemWriteAddressOut, // New for Rel 0 on 6/18
|
|
output SensorMemWriteEnableOut, // New for Rel 0 on 6/18
|
|
input [35:0] ControlMemDataIn,
|
|
input [35:0] Mem1ReadData,
|
|
input [35:0] Mem2ReadData,
|
|
input [17:0] SensorMemReadDataIn,
|
|
input SensorMemBusyIn,
|
|
|
|
output [8:0] SensorMemWriteDataOut, // New for Rel 0 on 6/18
|
|
output BusyOut,
|
|
output DataMem1ReadEnable,
|
|
output DataMem2ReadEnable,
|
|
output DataMem1WriteEnable,
|
|
output DataMem2WriteEnable,
|
|
output [9:0] DataMem1ReadAddressOut,
|
|
output [9:0] DataMem1WriteAddressOut,
|
|
output [35:0] DataMem1WriteDataOut,
|
|
output [9:0] DataMem2ReadAddressOut,
|
|
output [9:0] DataMem2WriteAddressOut,
|
|
output [35:0] DataMem2WriteDataOut,
|
|
|
|
output reg FFE_clock_halfperiod,
|
|
|
|
input MultClockIn,
|
|
input [3:0] MultStateIn,
|
|
|
|
// Status data
|
|
input SMBusyIn,
|
|
output reg SMOverrunOut,
|
|
|
|
// CM FIFO controls
|
|
output [17:0] CMWriteDataOut,
|
|
output CMWriteEnableOut,
|
|
|
|
output [7:0] InterruptMsgOut,
|
|
|
|
// interface to ASSP multiplier
|
|
output [31:0] mult_in1,
|
|
output [31:0] mult_in2,
|
|
output mult_enable,
|
|
input [63:0] mult_out,
|
|
|
|
|
|
output TP1,
|
|
output TP2,
|
|
output TP3
|
|
);
|
|
|
|
|
|
// compiler directives related to CM size:
|
|
// ENABLE_FFE_F0_CM_SIZE_2K
|
|
// ENABLE_FFE_F0_CM_SIZE_4K
|
|
// ENABLE_FFE_F0_CM_SIZE_3K (future support if needed)
|
|
|
|
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
// 4k CM
|
|
reg [11:0] xPC;
|
|
wire [11:0] xJumpAddress;
|
|
reg [11:0] PC_BG;
|
|
`else
|
|
// 2k CM (3k CM support may be added in the future)
|
|
reg [10:0] xPC;
|
|
wire [10:0] xJumpAddress;
|
|
reg [10:0] PC_BG;
|
|
`endif
|
|
|
|
|
|
reg BusyOut_reg, BusyOut_r1;
|
|
|
|
reg Start_r1, Start_r2, Start_r3;
|
|
wire [31:0] Mem1ReadDataToALU;
|
|
wire [31:0] Mem2ReadDataToALU;
|
|
wire [8:0] MicroOpCode;
|
|
wire [63:0] Signals; // the width of Signals is defined to be way larger than it needs to be (today), extra bits should get optimized out.
|
|
wire [8:0] xIndexRegister;
|
|
wire [31:0] xWriteData;
|
|
wire xJumpFlag;
|
|
wire [35:0] Mem1ReadDataX;
|
|
wire [35:0] Mem2ReadDataX;
|
|
|
|
reg [7:0] InterruptMsg_reg;
|
|
reg StartSM_reg;
|
|
|
|
reg [15:0] TimeStamp_r1, TimeStamp_r2, TimeStamp_synced;
|
|
|
|
|
|
reg f5_BG;
|
|
reg f2_BG;
|
|
reg BGcontinue_pending;
|
|
reg BGsave_pending;
|
|
reg BGstop_pending;
|
|
reg BG_active;
|
|
reg Start_pending;
|
|
wire Start_detected;
|
|
wire Save_BG_registers;
|
|
wire Restore_BG_registers;
|
|
wire Clear_PC;
|
|
wire Disable_DataMem_WrEn;
|
|
|
|
reg [2:0] Thread_switch_cnt;
|
|
parameter [2:0] THREAD_SWITCH_CNT_DONE = 3'b111;
|
|
|
|
|
|
// standard-depth DM addresses (9 bits)
|
|
wire [8:0] DataMem1ReadAddressOut_std;
|
|
wire [8:0] DataMem1WriteAddressOut_std;
|
|
wire [8:0] DataMem2ReadAddressOut_std;
|
|
wire [8:0] DataMem2WriteAddressOut_std;
|
|
|
|
wire [9:0] DataMem1WriteAddressOut_trans;
|
|
wire [9:0] DataMem2WriteAddressOut_trans;
|
|
wire [9:0] DataMem1ReadAddressOut_trans;
|
|
wire [9:0] DataMem2ReadAddressOut_trans;
|
|
reg [9:0] DataMem2ReadAddressOut_trans_hold;
|
|
reg DataMem1ReadAddressOut_trans_MSB_r1;
|
|
|
|
wire [9:0] DataMem1WriteAddressOut_mux;
|
|
wire [9:0] DataMem1ReadAddressOut_mux;
|
|
|
|
wire DataMem1ReadEnable_mux;
|
|
wire DataMem1WriteEnable_mux;
|
|
|
|
wire [9:0] DataMem1WriteAddressOut_split;
|
|
wire [9:0] DataMem2WriteAddressOut_split;
|
|
wire [9:0] DataMem1ReadAddressOut_split;
|
|
wire [9:0] DataMem2ReadAddressOut_split;
|
|
|
|
wire DataMem1ReadEnable_split;
|
|
wire DataMem1WriteEnable_split;
|
|
wire DataMem2ReadEnable_split;
|
|
wire DataMem2WriteEnable_split;
|
|
|
|
//reg FFE_clock_halfperiod;
|
|
|
|
wire DataMem1ReadEnable_std;
|
|
wire DataMem2ReadEnable_std;
|
|
reg DataMem2ReadEnable_std_hold;
|
|
wire DataMem1WriteEnable_std;
|
|
wire DataMem2WriteEnable_std;
|
|
|
|
reg [31:0] Mem1ReadData_latched;
|
|
reg [31:0] Mem2ReadData_latched;
|
|
|
|
wire ClockIn_dly1;
|
|
wire ClockIn_dly2;
|
|
wire ClockIn_dly3;
|
|
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
reg SensorMemReadEnable_reg;
|
|
reg SensorMemWriteEnable_reg;
|
|
reg [9:0] SensorMemReadAddress_reg;
|
|
reg [9:0] SensorMemWriteAddress_reg;
|
|
reg CMWriteEnable_reg;
|
|
`endif
|
|
|
|
// compiler options, from rtl_defines.v
|
|
// ENABLE_FFE_F0_EXTENDED_DM
|
|
// ENABLE_FFE_F0_SINGLE_DM
|
|
// ENABLE_FFE_F0_PROGRAMMABLE_SEG0_OFFSET
|
|
// FFE_F0_SEG0_OFFSET [value]
|
|
|
|
`ifdef FFE_F0_SEG0_OFFSET
|
|
parameter [8:0] Segment0_offset = `FFE_F0_SEG0_OFFSET;
|
|
`endif
|
|
|
|
`ifdef ENABLE_FFE_F0_EXTENDED_DM
|
|
reg [9:0] CurrentSegment_offset;
|
|
reg DataMem2ReadAddress_MSB_r1;
|
|
`endif
|
|
|
|
`ifdef ENABLE_FFE_F0_SINGLE_DM
|
|
reg [31:16] WriteData_latched;
|
|
reg sig37_latched;
|
|
`endif
|
|
|
|
|
|
// sync the timestamp to this clock domain
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
always @(posedge Clock_x2In) begin
|
|
`else
|
|
always @(posedge ClockIn) begin
|
|
`endif
|
|
TimeStamp_r1 <= TimeStampIn;
|
|
TimeStamp_r2 <= TimeStamp_r1;
|
|
if (TimeStamp_r1 == TimeStamp_r2)
|
|
TimeStamp_synced <= TimeStamp_r2;
|
|
else
|
|
TimeStamp_synced <= TimeStamp_synced;
|
|
end
|
|
|
|
|
|
FFE_ALU u_FFE_ALU (
|
|
.xReadData1 ( Mem1ReadDataToALU[31:0] ),
|
|
.xReadData2 ( Mem2ReadDataToALU[31:0] ),
|
|
.signals ( Signals ),
|
|
.ClockIn ( ClockIn ),
|
|
.Clock_x2In ( Clock_x2In ),
|
|
.FFE_clock_halfperiod ( FFE_clock_halfperiod ),
|
|
.MultClockIn ( MultClockIn ),
|
|
.MultStateIn ( MultStateIn ),
|
|
.TimeStampIn ( TimeStamp_synced ),
|
|
.MailboxIn ( MailboxIn ),
|
|
.SM_InterruptIn ( SM_InterruptIn ),
|
|
.xIndexRegister ( xIndexRegister ),
|
|
.xWriteData ( xWriteData ),
|
|
.xJumpFlag ( xJumpFlag ),
|
|
.Save_BG_registers ( Save_BG_registers ),
|
|
.Restore_BG_registers ( Restore_BG_registers ),
|
|
|
|
.mult_in1 ( mult_in1 ),
|
|
.mult_in2 ( mult_in2 ),
|
|
.mult_enable ( mult_enable ),
|
|
.mult_out ( mult_out )
|
|
);
|
|
|
|
decodeMicroOpCode U_decodeMicroOpCode (
|
|
.MicroOpCode ( MicroOpCode ),
|
|
.Signals ( Signals )
|
|
);
|
|
|
|
// Fetch Micro OpCode from Control Memory
|
|
// then needs to be decoded for the various control signals to the ALU (these are called 'signals')
|
|
assign MicroOpCode = BusyOut_reg ? ControlMemDataIn[8:0] : 9'b0; // xMicroOpCode (hold at zero if FFE is not running because of single port ASSP RAMs
|
|
|
|
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
// 4k CM
|
|
assign xJumpAddress = ControlMemDataIn[20:9];
|
|
`else
|
|
// 2k CM
|
|
assign xJumpAddress = ControlMemDataIn[19:9];
|
|
`endif
|
|
|
|
|
|
// standard (legacy) control/address signals for the DM's
|
|
|
|
assign DataMem1ReadEnable_std = Signals[20];
|
|
assign DataMem2ReadEnable_std = Signals[21];
|
|
assign DataMem1WriteEnable_std = Disable_DataMem_WrEn ? 1'b0 : (Signals[22] && BusyOut_reg);
|
|
assign DataMem2WriteEnable_std = Disable_DataMem_WrEn ? 1'b0 : (Signals[23] && BusyOut_reg);
|
|
|
|
assign DataMem1WriteAddressOut_std = Signals[34] ? (ControlMemDataIn[17:9] + xIndexRegister) : ControlMemDataIn[17:9];
|
|
assign DataMem2WriteAddressOut_std = Signals[34] ? (ControlMemDataIn[17:9] + xIndexRegister) : ControlMemDataIn[17:9];
|
|
assign DataMem1ReadAddressOut_std = Signals[28] ? (ControlMemDataIn[26:18] + xIndexRegister) : ControlMemDataIn[26:18];
|
|
assign DataMem2ReadAddressOut_std = Signals[29] ? (ControlMemDataIn[35:27] + xIndexRegister) : ControlMemDataIn[35:27];
|
|
|
|
|
|
// translate DM read/write addresses if an extended-length DM is specified
|
|
`ifdef ENABLE_FFE_F0_EXTENDED_DM
|
|
// extended-length DM
|
|
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
always @(posedge Clock_x2In or posedge ResetIn)
|
|
if (ResetIn)
|
|
CurrentSegment_offset <= 0;
|
|
else
|
|
if (Signals[44] && FFE_clock_halfperiod) // seg_offset being written
|
|
CurrentSegment_offset <= ControlMemDataIn[32:23];
|
|
`else
|
|
always @(posedge ClockIn or posedge ResetIn)
|
|
if (ResetIn)
|
|
CurrentSegment_offset <= 0;
|
|
else
|
|
if (Signals[44]) // seg_offset being written
|
|
CurrentSegment_offset <= ControlMemDataIn[32:23];
|
|
`endif
|
|
|
|
// translate addresses to handle extended data memory(ies)
|
|
assign DataMem1WriteAddressOut_trans = (DataMem1WriteAddressOut_std < Segment0_offset) ?
|
|
{1'b0, DataMem1WriteAddressOut_std[8:0]} :
|
|
({1'b0, DataMem1WriteAddressOut_std} + {1'b0, CurrentSegment_offset});
|
|
assign DataMem2WriteAddressOut_trans = (DataMem2WriteAddressOut_std < Segment0_offset) ?
|
|
{1'b0, DataMem2WriteAddressOut_std[8:0]} :
|
|
({1'b0, DataMem2WriteAddressOut_std} + {1'b0, CurrentSegment_offset});
|
|
assign DataMem1ReadAddressOut_trans = (DataMem1ReadAddressOut_std < Segment0_offset) ?
|
|
{1'b0, DataMem1ReadAddressOut_std[8:0]} :
|
|
({1'b0, DataMem1ReadAddressOut_std} + {1'b0, CurrentSegment_offset});
|
|
assign DataMem2ReadAddressOut_trans = (DataMem2ReadAddressOut_std < Segment0_offset) ?
|
|
{1'b0, DataMem2ReadAddressOut_std[8:0]} :
|
|
({1'b0, DataMem2ReadAddressOut_std} + {1'b0, CurrentSegment_offset});
|
|
|
|
`else
|
|
// standard-length DM (could be single or double)
|
|
|
|
assign DataMem1WriteAddressOut_trans = {1'b0, DataMem1WriteAddressOut_std};
|
|
assign DataMem2WriteAddressOut_trans = {1'b0, DataMem2WriteAddressOut_std};
|
|
assign DataMem1ReadAddressOut_trans = {1'b0, DataMem1ReadAddressOut_std};
|
|
assign DataMem2ReadAddressOut_trans = {1'b0, DataMem2ReadAddressOut_std};
|
|
|
|
`endif
|
|
|
|
|
|
// mux the DM1/DM2 addresses into a single logical DM1 address if a single-DM design is specified
|
|
`ifdef ENABLE_FFE_F0_SINGLE_DM
|
|
// single DM
|
|
|
|
// keep track of the half-period (0 or 1) within a single FFE clock, by using the 2x FFE clock.
|
|
// FFE_clock_halfperiod should be 0 when the 1x clock is low, 1 when the 1x clock is high (it's registered here to help eliminate timing issues).
|
|
buff buff_clockin_dly1 (.A(ClockIn), .Q(ClockIn_dly1));
|
|
buff buff_clockin_dly2 (.A(ClockIn_dly1), .Q(ClockIn_dly2));
|
|
buff buff_clockin_dly3 (.A(ClockIn_dly2), .Q(ClockIn_dly3));
|
|
//pragma attribute buff_clockin_dly1 dont_touch true
|
|
//pragma attribute buff_clockin_dly2 dont_touch true
|
|
//pragma attribute buff_clockin_dly3 dont_touch true
|
|
assign #3 ClockIn_dly4 = ClockIn_dly3;
|
|
always @(posedge Clock_x2In or posedge ResetIn)
|
|
if (ResetIn)
|
|
FFE_clock_halfperiod <= 0;
|
|
else
|
|
FFE_clock_halfperiod <= #1 ClockIn_dly4;
|
|
/*
|
|
if (BusyOut_reg)
|
|
FFE_clock_halfperiod <= !FFE_clock_halfperiod;
|
|
else
|
|
FFE_clock_halfperiod <= 0;
|
|
*/
|
|
|
|
always @(posedge Clock_x2In or posedge ResetIn)
|
|
if (ResetIn) begin
|
|
DataMem2ReadAddressOut_trans_hold <= 0;
|
|
DataMem2ReadEnable_std_hold <= 0;
|
|
end
|
|
else begin
|
|
if (!FFE_clock_halfperiod) begin
|
|
DataMem2ReadAddressOut_trans_hold <= DataMem2ReadAddressOut_trans;
|
|
DataMem2ReadEnable_std_hold <= DataMem2ReadEnable_std;
|
|
end
|
|
end
|
|
|
|
// on half-period 0, drive the DM1 read address and read enable
|
|
// on half-period 1, drive the DM2 read address and read enable
|
|
// drive the write address on both half-periods, and the write enable on half-period 0
|
|
assign DataMem1ReadAddressOut_mux = FFE_clock_halfperiod ? DataMem2ReadAddressOut_trans_hold : DataMem1ReadAddressOut_trans;
|
|
assign DataMem1ReadEnable_mux = FFE_clock_halfperiod ? DataMem2ReadEnable_std_hold : DataMem1ReadEnable_std;
|
|
assign DataMem1WriteAddressOut_mux = DataMem1WriteAddressOut_trans; // note: DM1 write address = DM2 write address
|
|
assign DataMem1WriteEnable_mux = FFE_clock_halfperiod ? 0 : DataMem1WriteEnable_std;
|
|
|
|
`else
|
|
// double DM
|
|
|
|
// FFE_clock_halfperiod should never be used in this case. Assign it to 0.
|
|
always @(*)
|
|
FFE_clock_halfperiod <= 0;
|
|
`endif
|
|
|
|
|
|
// split the muxed RAM control signals across both physical memories if an extended-depth DM is specified.
|
|
// (if an extended-depth DM is specified, it must also be a single DM)
|
|
`ifdef ENABLE_FFE_F0_EXTENDED_DM
|
|
// extended-length DM
|
|
// note that the DM2 "_mux" signals are not defined, since it's assumed that an extended-length DM is also a single-DM
|
|
|
|
assign DataMem1ReadAddressOut_split = DataMem1ReadAddressOut_mux;
|
|
assign DataMem1ReadEnable_split = (DataMem1ReadAddressOut_mux[9] == 1'b0) ? DataMem1ReadEnable_mux : 1'b0;
|
|
assign DataMem1WriteAddressOut_split = DataMem1WriteAddressOut_mux;
|
|
// assign DataMem1WriteEnable_split = (DataMem1WriteAddressOut_mux[9] == 1'b0) ? DataMem1WriteEnable_split : 1'b0; // original
|
|
assign DataMem1WriteEnable_split = (DataMem1WriteAddressOut_mux[9] == 1'b0) ? DataMem1WriteEnable_mux : 1'b0; // Anthony Le 11-01-2014
|
|
|
|
assign DataMem2ReadAddressOut_split = DataMem1ReadAddressOut_mux;
|
|
assign DataMem2ReadEnable_split = (DataMem1ReadAddressOut_mux[9] == 1'b1) ? DataMem1ReadEnable_mux : 1'b0;
|
|
assign DataMem2WriteAddressOut_split = DataMem1WriteAddressOut_mux;
|
|
// assign DataMem2WriteEnable_split = (DataMem1WriteAddressOut_mux[9] == 1'b1) ? DataMem1WriteEnable_split : 1'b0; // original
|
|
assign DataMem2WriteEnable_split = (DataMem1WriteAddressOut_mux[9] == 1'b1) ? DataMem1WriteEnable_mux : 1'b0; // Anthony Le 11-01-2014
|
|
`endif
|
|
|
|
|
|
// drive the outputs for the DM control/address
|
|
`ifdef ENABLE_FFE_F0_EXTENDED_DM
|
|
// extended-length DM (must be single DM as well)
|
|
|
|
// must use the translated then muxed then split signals...
|
|
|
|
assign DataMem1ReadEnable = DataMem1ReadEnable_split;
|
|
assign DataMem1WriteEnable = DataMem1WriteEnable_split;
|
|
assign DataMem1WriteAddressOut = DataMem1WriteAddressOut_split;
|
|
assign DataMem1ReadAddressOut = DataMem1ReadAddressOut_split;
|
|
|
|
assign DataMem2ReadEnable = DataMem2ReadEnable_split;
|
|
assign DataMem2WriteEnable = DataMem2WriteEnable_split;
|
|
assign DataMem2WriteAddressOut = DataMem2WriteAddressOut_split;
|
|
assign DataMem2ReadAddressOut = DataMem2ReadAddressOut_split;
|
|
|
|
`else
|
|
// standard-length DM
|
|
|
|
`ifdef ENABLE_FFE_F0_SINGLE_DM
|
|
// standard-length single DM
|
|
|
|
// must use the non-translated then muxed signals
|
|
// note that physical DM2 is unused, so those outputs are grounded.
|
|
|
|
assign DataMem1ReadEnable = DataMem1ReadEnable_mux;
|
|
assign DataMem1WriteEnable = DataMem1WriteEnable_mux;
|
|
assign DataMem1WriteAddressOut = DataMem1WriteAddressOut_mux;
|
|
assign DataMem1ReadAddressOut = DataMem1ReadAddressOut_mux;
|
|
|
|
assign DataMem2ReadEnable = 0;
|
|
assign DataMem2WriteEnable = 0;
|
|
assign DataMem2WriteAddressOut = 0;
|
|
assign DataMem2ReadAddressOut = 0;
|
|
|
|
`else
|
|
// standard-length double DM (legacy)
|
|
|
|
// use the standard signals
|
|
|
|
assign DataMem1WriteAddressOut = {1'b0, DataMem1WriteAddressOut_std};
|
|
assign DataMem2WriteAddressOut = {1'b0, DataMem2WriteAddressOut_std};
|
|
assign DataMem1ReadAddressOut = {1'b0, DataMem1ReadAddressOut_std};
|
|
assign DataMem2ReadAddressOut = {1'b0, DataMem2ReadAddressOut_std};
|
|
|
|
assign DataMem1ReadEnable = DataMem1ReadEnable_std;
|
|
assign DataMem2ReadEnable = DataMem2ReadEnable_std;
|
|
assign DataMem1WriteEnable = DataMem1WriteEnable_std;
|
|
assign DataMem2WriteEnable = DataMem2WriteEnable_std;
|
|
|
|
`endif
|
|
|
|
`endif
|
|
|
|
|
|
`ifdef ENABLE_FFE_F0_SINGLE_DM
|
|
// single DM, extended or standard length
|
|
|
|
// hold the write data so it can be written to the CM FIFO or SM Mem correctly, when a single DM is used
|
|
always @(posedge Clock_x2In or posedge ResetIn)
|
|
if (ResetIn) begin
|
|
WriteData_latched <= 0;
|
|
sig37_latched <= 0;
|
|
end
|
|
else begin
|
|
if (!FFE_clock_halfperiod) begin
|
|
sig37_latched <= Signals[37];
|
|
//if (!FFE_clock_halfperiod && (CMWriteEnableOut || SensorMemWriteEnableOut || Signals[36]))
|
|
if (Signals[30] || Signals[40] || Signals[36]) // CM write or SM write or Interrupt msg write
|
|
WriteData_latched <= xWriteData[31:16];
|
|
end
|
|
end
|
|
|
|
//assign CMWriteDataOut = {1'b0, WriteData_latched[31:24], Signals[37], WriteData_latched[23:16]};
|
|
assign CMWriteDataOut = {1'b0, WriteData_latched[31:24], sig37_latched, WriteData_latched[23:16]};
|
|
assign SensorMemWriteDataOut = WriteData_latched[24:16];
|
|
|
|
`else
|
|
// double DM (legacy)
|
|
|
|
assign CMWriteDataOut = {1'b0, xWriteData[31:24], Signals[37], xWriteData[23:16]};
|
|
assign SensorMemWriteDataOut = xWriteData[24:16];
|
|
`endif
|
|
|
|
|
|
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
// 4k CM
|
|
|
|
// this is done to make sure that all of these signals are stable when the 1x clock occurs.
|
|
always @(posedge Clock_x2In or posedge ResetIn)
|
|
if (ResetIn) begin
|
|
SensorMemReadEnable_reg <= 0;
|
|
SensorMemWriteEnable_reg <= 0;
|
|
SensorMemReadAddress_reg <= 0;
|
|
SensorMemWriteAddress_reg <= 0;
|
|
CMWriteEnable_reg <= 0;
|
|
end
|
|
else begin
|
|
if (!FFE_clock_halfperiod ) begin
|
|
SensorMemReadEnable_reg <= DataMem1ReadEnable_std;
|
|
SensorMemWriteEnable_reg <= Disable_DataMem_WrEn ? 1'b0 : (Signals[40] && BusyOut_reg);
|
|
SensorMemReadAddress_reg <= Signals[28] ? (ControlMemDataIn[27:18] + xIndexRegister) : ControlMemDataIn[27:18];
|
|
SensorMemWriteAddress_reg <= Signals[34] ? (ControlMemDataIn[18:9] + xIndexRegister) : ControlMemDataIn[18:9];
|
|
CMWriteEnable_reg <= Disable_DataMem_WrEn ? 1'b0 : (Signals[30] && BusyOut_reg); // Write enable to CM FIFO
|
|
end
|
|
end
|
|
|
|
assign SensorMemReadEnableOut = SensorMemReadEnable_reg;
|
|
assign SensorMemWriteEnableOut = SensorMemWriteEnable_reg;
|
|
assign SensorMemReadAddressOut = SensorMemReadAddress_reg;
|
|
assign SensorMemWriteAddressOut = SensorMemWriteAddress_reg;
|
|
assign CMWriteEnableOut = CMWriteEnable_reg;
|
|
|
|
`else
|
|
// 2k CM
|
|
|
|
assign SensorMemReadEnableOut = DataMem1ReadEnable_std;
|
|
assign SensorMemWriteEnableOut = Disable_DataMem_WrEn ? 1'b0 : (Signals[40] && BusyOut_reg);
|
|
assign SensorMemReadAddressOut = (Signals[28] ? (ControlMemDataIn[27:18] + xIndexRegister) : ControlMemDataIn[27:18]);
|
|
assign SensorMemWriteAddressOut = Signals[34] ? (ControlMemDataIn[18:9] + xIndexRegister) : ControlMemDataIn[18:9];
|
|
|
|
assign CMWriteEnableOut = Disable_DataMem_WrEn ? 1'b0 : (Signals[30] && BusyOut_reg); // Write enable to CM FIFO
|
|
`endif
|
|
|
|
|
|
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
// 4k CM
|
|
assign ControlMemAddressOut = xPC;
|
|
`else
|
|
// 2k CM
|
|
assign ControlMemAddressOut = {1'b0, xPC};
|
|
`endif
|
|
|
|
assign DataMem1WriteDataOut = {4'b0000,xWriteData};
|
|
assign DataMem2WriteDataOut = {4'b0000,xWriteData};
|
|
|
|
|
|
|
|
// latch the read data from the DM(s)
|
|
`ifdef ENABLE_FFE_F0_SINGLE_DM
|
|
`ifdef ENABLE_FFE_F0_EXTENDED_DM
|
|
// extended-length single-DM
|
|
|
|
always @(posedge Clock_x2In or posedge ResetIn)
|
|
if (ResetIn)
|
|
DataMem1ReadAddressOut_trans_MSB_r1 <= 0;
|
|
else
|
|
if (!FFE_clock_halfperiod)
|
|
DataMem1ReadAddressOut_trans_MSB_r1 <= DataMem1ReadAddressOut_trans[9];
|
|
|
|
|
|
always @(posedge Clock_x2In or posedge ResetIn)
|
|
if (ResetIn)
|
|
Mem1ReadData_latched <= 0;
|
|
else
|
|
if (FFE_clock_halfperiod)
|
|
Mem1ReadData_latched <= DataMem1ReadAddressOut_trans_MSB_r1 ? Mem2ReadData : Mem1ReadData; // read from the correct physical DM
|
|
|
|
// store the (logical) DM2 read address' MSB, so it can be used on the following clock to select the correct physical DM
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
always @(posedge Clock_x2In or posedge ResetIn)
|
|
if (ResetIn)
|
|
DataMem2ReadAddress_MSB_r1 <= 0;
|
|
else
|
|
if (FFE_clock_halfperiod)
|
|
DataMem2ReadAddress_MSB_r1 <= DataMem2ReadAddressOut_trans[9];
|
|
`else
|
|
always @(posedge ClockIn or posedge ResetIn)
|
|
if (ResetIn)
|
|
DataMem2ReadAddress_MSB_r1 <= 0;
|
|
else
|
|
DataMem2ReadAddress_MSB_r1 <= DataMem2ReadAddressOut_trans[9];
|
|
`endif
|
|
|
|
|
|
|
|
|
|
always @(*)
|
|
Mem2ReadData_latched <= DataMem2ReadAddress_MSB_r1 ? Mem2ReadData : Mem1ReadData; // read from the correct physical DM
|
|
// note that Mem2ReadData_latched will only be valid on the first half-period
|
|
// note that ReadData2 can be registered as well, to make FFE clock-to-clock timing easier (at the cost of more FFs).
|
|
|
|
`else
|
|
// standard-length single-DM, latch & hold at the appropriate half-periods
|
|
always @(posedge Clock_x2In or posedge ResetIn)
|
|
if (ResetIn)
|
|
Mem1ReadData_latched <= 0;
|
|
else
|
|
if (FFE_clock_halfperiod)
|
|
Mem1ReadData_latched <= Mem1ReadData;
|
|
else
|
|
Mem1ReadData_latched <= Mem1ReadData_latched;
|
|
|
|
always @(*)
|
|
Mem2ReadData_latched <= Mem1ReadData;
|
|
// note that ReadData2 can be registered as well, to make FFE clock-to-clock timing easier (at the cost of more FFs).
|
|
`endif
|
|
|
|
`else
|
|
// standard-length double-DM, pass-thru
|
|
always @(*) begin
|
|
Mem1ReadData_latched <= Mem1ReadData;
|
|
Mem2ReadData_latched <= Mem2ReadData;
|
|
end
|
|
`endif
|
|
|
|
|
|
assign Mem1ReadDataX = Mem1ReadData_latched[31:0];
|
|
//a mux that switches between data read from FFEDM2 or SMSM
|
|
assign Mem2ReadDataX = Signals[27] ? {SensorMemReadDataIn[16:9], SensorMemReadDataIn[7:0], 16'b0} : Mem2ReadData_latched[31:0];
|
|
|
|
|
|
assign Mem1ReadDataToALU = Mem1ReadDataX;
|
|
assign Mem2ReadDataToALU = Mem2ReadDataX;
|
|
|
|
|
|
|
|
|
|
// toggle StartSM each time Signals[31] is active
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
always @(posedge Clock_x2In or posedge ResetIn)
|
|
if (ResetIn)
|
|
StartSM_reg <= 0;
|
|
else
|
|
if (FFE_clock_halfperiod && Signals[31])
|
|
StartSM_reg <= !StartSM_reg;
|
|
else
|
|
StartSM_reg <= StartSM_reg;
|
|
|
|
`else
|
|
always @(posedge ClockIn or posedge ResetIn)
|
|
if (ResetIn)
|
|
StartSM_reg <= 0;
|
|
else
|
|
if (Signals[31])
|
|
StartSM_reg <= !StartSM_reg;
|
|
else
|
|
StartSM_reg <= StartSM_reg;
|
|
|
|
`endif
|
|
|
|
assign StartSMOut = StartSM_reg;
|
|
|
|
|
|
// de-glitch the interrupt msg signal since it comes out of the decoder and data mem
|
|
`ifdef ENABLE_FFE_F0_SINGLE_DM
|
|
// single DM
|
|
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
// 4k CM
|
|
always @(posedge Clock_x2In)
|
|
InterruptMsg_reg <= (FFE_clock_halfperiod && Signals[36]) ? WriteData_latched[23:16] : 8'b0;
|
|
`else
|
|
// 2k CM
|
|
always @(posedge ClockIn)
|
|
InterruptMsg_reg <= Signals[36] ? WriteData_latched[23:16] : 8'b0;
|
|
`endif
|
|
|
|
`else
|
|
// double DM, legacy behavior
|
|
always @(posedge ClockIn)
|
|
InterruptMsg_reg <= Signals[36] ? xWriteData[23:16] : 8'b0;
|
|
`endif
|
|
|
|
assign InterruptMsgOut = InterruptMsg_reg;
|
|
|
|
|
|
// sync to local clock
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
always @(posedge Clock_x2In) begin
|
|
`else
|
|
always @(posedge ClockIn) begin
|
|
`endif
|
|
Start_r1 <= StartIn;
|
|
Start_r2 <= Start_r1;
|
|
Start_r3 <= Start_r2;
|
|
end
|
|
|
|
assign Start_detected = (Start_r1 != Start_r2) || (Start_r2 != Start_r3);
|
|
|
|
|
|
// Program Counter to step through the Control Memory
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
always @(posedge Clock_x2In or posedge ResetIn) begin
|
|
`else
|
|
always @(posedge ClockIn or posedge ResetIn) begin
|
|
`endif
|
|
|
|
if (ResetIn) begin
|
|
xPC <= 0;
|
|
BusyOut_reg <= 1'b0;
|
|
BusyOut_r1 <= 1'b0;
|
|
ControlMemReadEnableOut <= 1'b0;
|
|
SMOverrunOut <= 1'b0;
|
|
end else
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
if (FFE_clock_halfperiod)
|
|
`endif
|
|
begin
|
|
BusyOut_r1 <= BusyOut_reg;
|
|
if (BusyOut_reg && BusyOut_r1) begin // make sure the 1st control word appears on the RAM outputs... requires one clock cycle after the read enable is turned on.
|
|
if (!Signals[32]) begin // !Signals[32] = continue running = !STOP
|
|
if (Restore_BG_registers)
|
|
xPC <= PC_BG;
|
|
else
|
|
if (Clear_PC)
|
|
xPC <= 0;
|
|
else
|
|
// hold the PC while switching threads
|
|
if (BG_active &&
|
|
((Start_detected || BGsave_pending) ||
|
|
(BGcontinue_pending && (Thread_switch_cnt != THREAD_SWITCH_CNT_DONE))))
|
|
xPC <= xPC;
|
|
else
|
|
if (xJumpFlag)
|
|
xPC <= xJumpAddress;
|
|
else
|
|
xPC <= xPC + 1;
|
|
end else begin // Signals[32] = STOP
|
|
xPC <= 0;
|
|
if (!BG_active) begin // FG mode
|
|
BusyOut_reg <= 1'b0;
|
|
ControlMemReadEnableOut <= 1'b0;
|
|
end else begin // BG mode
|
|
ControlMemReadEnableOut <= 1'b1;
|
|
if (BGstop_pending && (Thread_switch_cnt == THREAD_SWITCH_CNT_DONE) && !Start_pending && !Start_detected)
|
|
BusyOut_reg <= 1'b0;
|
|
else
|
|
BusyOut_reg <= 1'b1;
|
|
end
|
|
end
|
|
end else // new start condition not detected, keep running
|
|
if (Start_detected) begin
|
|
if (SMBusyIn) begin
|
|
SMOverrunOut <= 1'b1;
|
|
end else begin
|
|
BusyOut_reg <= 1'b1;
|
|
ControlMemReadEnableOut <= 1'b1;
|
|
end
|
|
end
|
|
|
|
end
|
|
end
|
|
|
|
assign BusyOut = BusyOut_reg;
|
|
|
|
|
|
|
|
// --- BG thread support
|
|
|
|
// Signals[42] = SetBGflag (instruction modifier)
|
|
// Signals[43] = BGcontinue (instruction)
|
|
|
|
|
|
// Start_pending, flag to latch the start event, in case it happens right as we're switching to the BG thread or while running the BG thread.
|
|
// This needs to be latched so the FG thread can be started immediately once we've switched out of the BG thread.
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
always @(posedge Clock_x2In or posedge ResetIn)
|
|
`else
|
|
always @(posedge ClockIn or posedge ResetIn)
|
|
`endif
|
|
if (ResetIn)
|
|
Start_pending <= 0;
|
|
else
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
if (FFE_clock_halfperiod)
|
|
`endif
|
|
case (Start_pending)
|
|
1'b0: if (Start_detected && // start detected AND...
|
|
(Signals[42] || // ...SetBGflag active (about to start or continue BG)...OR...
|
|
BG_active)) // ...BG active (switching to BG, running BG, about to stop/end BG, stopping BG)
|
|
Start_pending <= 1;
|
|
1'b1: if (!BG_active) // clear this flag when BG_active goes away
|
|
Start_pending <= 0;
|
|
endcase
|
|
|
|
|
|
// BG_pending counter, used instead of individual state machines for each type of context switch
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
always @(posedge Clock_x2In or posedge ResetIn)
|
|
`else
|
|
always @(posedge ClockIn or posedge ResetIn)
|
|
`endif
|
|
if (ResetIn)
|
|
Thread_switch_cnt <= 0;
|
|
else
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
if (FFE_clock_halfperiod)
|
|
`endif
|
|
if (BGsave_pending || BGcontinue_pending || BGstop_pending)
|
|
if (Thread_switch_cnt == THREAD_SWITCH_CNT_DONE)
|
|
Thread_switch_cnt <= 0;
|
|
else
|
|
Thread_switch_cnt <= Thread_switch_cnt + 1;
|
|
else
|
|
Thread_switch_cnt <= 0;
|
|
|
|
|
|
// BGcontinue_pending, flag that goes active while resuming the BG thread (BGcontinue instruction)
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
always @(posedge Clock_x2In or posedge ResetIn)
|
|
`else
|
|
always @(posedge ClockIn or posedge ResetIn)
|
|
`endif
|
|
if (ResetIn)
|
|
BGcontinue_pending <= 0;
|
|
else
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
if (FFE_clock_halfperiod)
|
|
`endif
|
|
case (BGcontinue_pending)
|
|
1'b0: if (Signals[43])
|
|
BGcontinue_pending <= 1;
|
|
1'b1: if (Thread_switch_cnt == THREAD_SWITCH_CNT_DONE)
|
|
BGcontinue_pending <= 0;
|
|
endcase
|
|
|
|
|
|
// BGsave_pending, flag that goes active while saving the state of the BG thread (BG_continue or BG thread interrupted by the sample timer)
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
always @(posedge Clock_x2In or posedge ResetIn)
|
|
`else
|
|
always @(posedge ClockIn or posedge ResetIn)
|
|
`endif
|
|
if (ResetIn)
|
|
BGsave_pending <= 0;
|
|
else
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
if (FFE_clock_halfperiod)
|
|
`endif
|
|
case (BGsave_pending)
|
|
1'b0: if (BG_active && // in the BG thread...AND...
|
|
(Start_detected || // ...started detected...OR...
|
|
(BGcontinue_pending && (Thread_switch_cnt == THREAD_SWITCH_CNT_DONE) && Start_pending))) // ...about to complete BGcontinue AND start was detected
|
|
BGsave_pending <= 1;
|
|
1'b1: if (Thread_switch_cnt == THREAD_SWITCH_CNT_DONE)
|
|
BGsave_pending <= 0;
|
|
endcase
|
|
|
|
|
|
// BGstop_pending, flag that goes active while stopping the BG thread (normal completion)
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
always @(posedge Clock_x2In or posedge ResetIn)
|
|
`else
|
|
always @(posedge ClockIn or posedge ResetIn)
|
|
`endif
|
|
if (ResetIn)
|
|
BGstop_pending <= 0;
|
|
else
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
if (FFE_clock_halfperiod)
|
|
`endif
|
|
case (BGstop_pending)
|
|
1'b0: if (BG_active && Signals[32])
|
|
BGstop_pending <= 1;
|
|
1'b1: if (Thread_switch_cnt == THREAD_SWITCH_CNT_DONE)
|
|
BGstop_pending <= 0;
|
|
endcase
|
|
|
|
|
|
|
|
// BG_active, flag that is active while switching to, in, or switching from, the BG thread
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
always @(posedge Clock_x2In or posedge ResetIn)
|
|
`else
|
|
always @(posedge ClockIn or posedge ResetIn)
|
|
`endif
|
|
if (ResetIn)
|
|
BG_active <= 0;
|
|
else
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
if (FFE_clock_halfperiod)
|
|
`endif
|
|
case (BG_active)
|
|
1'b0: if (Signals[42]) // SetBGactive (entering BG mode, either via start BG or continue BG)
|
|
BG_active <= 1;
|
|
1'b1: if ((BGsave_pending || BGstop_pending) && (Thread_switch_cnt == THREAD_SWITCH_CNT_DONE)) // done switching out of BG mode
|
|
BG_active <= 0;
|
|
endcase
|
|
|
|
|
|
// Control signal used to save the BG copy of the PC and ALU flags
|
|
assign Save_BG_registers = (BGsave_pending && (Thread_switch_cnt == 1));
|
|
// clock-by-clock sequence of events:
|
|
// {BGsave_pending,Thread_switch_cnt}
|
|
// 0,0 - Start detected, last BG instruction, hold PC
|
|
// 1,0 - hold PC, disable DataMemWrEn
|
|
// 1,1 - hold PC, save PC & flags (Save_BG_registers active), disable DataMemWrEn
|
|
// 1,2 - clear PC, disable DataMemWrEn
|
|
// 1,3 - hold PC (driving into CodeMem), disable DataMemWrEn
|
|
// 1,4 - hold PC (CodeMem available, driving DataMem), disable DataMemWrEn
|
|
// 1,5 - hold PC (DataMem available), disable DataMemWrEn
|
|
// 1,6 - hold PC (extraneous), disable DataMemWrEn
|
|
// 1,7 - hold PC (extraneous), disable DataMemWrEn
|
|
// 0,0 - continue running normally (now in FG thread)
|
|
|
|
// Control signal used to restore the BG state of the PC and ALU flags
|
|
assign Restore_BG_registers = (BGcontinue_pending && (Thread_switch_cnt == 1));
|
|
// clock-by-clock sequence of events:
|
|
// {BGcontinue_pending,Thread_switch_cnt} - action(s)
|
|
// 0,0 - BGcontinue();
|
|
// 1,0 - NOP;, disable DataMemWrEn
|
|
// 1,1 - load PC & flags (Restore_BG_registers active), disable DataMemWrEn
|
|
// 1,2 - hold PC (driving into CodeMem), disable DataMemWrEn
|
|
// 1,3 - hold PC (CodeMem available, driving DataMem), disable DataMemWrEn
|
|
// 1,4 - hold PC (DataMem available), disable DataMemWrEn
|
|
// 1,5 - hold PC (extraneous), disable DataMemWrEn
|
|
// 1,6 - hold PC (extraneous), disable DataMemWrEn
|
|
// 1,7 - increment PC, disable DataMemWrEn
|
|
// 0,0 - continue running normally (now in BG thread)
|
|
|
|
|
|
// Control signal used to reset the PC (during BGstop or BGsave)
|
|
assign Clear_PC = ((BGsave_pending && (Thread_switch_cnt == 2)) || (BGstop_pending));
|
|
|
|
// Control signal used to disable the FFE DataMem write enable, while resuming the BG thread
|
|
assign Disable_DataMem_WrEn = (BGcontinue_pending);
|
|
|
|
|
|
// BG copy of the PC
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
always @(posedge Clock_x2In or posedge ResetIn)
|
|
`else
|
|
always @(posedge ClockIn or posedge ResetIn)
|
|
`endif
|
|
if (ResetIn)
|
|
PC_BG <= 0;
|
|
else
|
|
`ifdef ENABLE_FFE_F0_CM_SIZE_4K
|
|
if (FFE_clock_halfperiod)
|
|
`endif
|
|
if (Save_BG_registers)
|
|
PC_BG <= xPC;
|
|
else
|
|
PC_BG <= PC_BG;
|
|
|
|
|
|
|
|
// test points
|
|
assign TP1 = 0;
|
|
assign TP2 = 0;
|
|
assign TP3 = 0;
|
|
|
|
|
|
endmodule
|
|
|
|
|
|
|
|
|