diff options
Diffstat (limited to 'tests/openmsp430/rtl/omsp_clock_module.v')
-rw-r--r-- | tests/openmsp430/rtl/omsp_clock_module.v | 1058 |
1 files changed, 1058 insertions, 0 deletions
diff --git a/tests/openmsp430/rtl/omsp_clock_module.v b/tests/openmsp430/rtl/omsp_clock_module.v new file mode 100644 index 000000000..670c5e590 --- /dev/null +++ b/tests/openmsp430/rtl/omsp_clock_module.v @@ -0,0 +1,1058 @@ +//---------------------------------------------------------------------------- +// Copyright (C) 2009 , Olivier Girard +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions +// are met: +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the authors nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, +// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +// THE POSSIBILITY OF SUCH DAMAGE +// +//---------------------------------------------------------------------------- +// +// *File Name: omsp_clock_module.v +// +// *Module Description: +// Basic clock module implementation. +// +// *Author(s): +// - Olivier Girard, olgirard@gmail.com +// +//---------------------------------------------------------------------------- +// $Rev: 134 $ +// $LastChangedBy: olivier.girard $ +// $LastChangedDate: 2012-03-22 21:31:06 +0100 (Thu, 22 Mar 2012) $ +//---------------------------------------------------------------------------- +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_defines.v" +`endif + +module omsp_clock_module ( + +// OUTPUTs + aclk, // ACLK + aclk_en, // ACLK enable + cpu_en_s, // Enable CPU code execution (synchronous) + dbg_clk, // Debug unit clock + dbg_en_s, // Debug interface enable (synchronous) + dbg_rst, // Debug unit reset + dco_enable, // Fast oscillator enable + dco_wkup, // Fast oscillator wake-up (asynchronous) + lfxt_enable, // Low frequency oscillator enable + lfxt_wkup, // Low frequency oscillator wake-up (asynchronous) + mclk, // Main system clock + per_dout, // Peripheral data output + por, // Power-on reset + puc_pnd_set, // PUC pending set for the serial debug interface + puc_rst, // Main system reset + smclk, // SMCLK + smclk_en, // SMCLK enable + +// INPUTs + cpu_en, // Enable CPU code execution (asynchronous) + cpuoff, // Turns off the CPU + dbg_cpu_reset, // Reset CPU from debug interface + dbg_en, // Debug interface enable (asynchronous) + dco_clk, // Fast oscillator (fast clock) + lfxt_clk, // Low frequency oscillator (typ 32kHz) + mclk_enable, // Main System Clock enable + mclk_wkup, // Main System Clock wake-up (asynchronous) + oscoff, // Turns off LFXT1 clock input + per_addr, // Peripheral address + per_din, // Peripheral data input + per_en, // Peripheral enable (high active) + per_we, // Peripheral write enable (high active) + reset_n, // Reset Pin (low active, asynchronous) + scan_enable, // Scan enable (active during scan shifting) + scan_mode, // Scan mode + scg0, // System clock generator 1. Turns off the DCO + scg1, // System clock generator 1. Turns off the SMCLK + wdt_reset // Watchdog-timer reset +); + +// OUTPUTs +//========= +output aclk; // ACLK +output aclk_en; // ACLK enable +output cpu_en_s; // Enable CPU code execution (synchronous) +output dbg_clk; // Debug unit clock +output dbg_en_s; // Debug unit enable (synchronous) +output dbg_rst; // Debug unit reset +output dco_enable; // Fast oscillator enable +output dco_wkup; // Fast oscillator wake-up (asynchronous) +output lfxt_enable; // Low frequency oscillator enable +output lfxt_wkup; // Low frequency oscillator wake-up (asynchronous) +output mclk; // Main system clock +output [15:0] per_dout; // Peripheral data output +output por; // Power-on reset +output puc_pnd_set; // PUC pending set for the serial debug interface +output puc_rst; // Main system reset +output smclk; // SMCLK +output smclk_en; // SMCLK enable + +// INPUTs +//========= +input cpu_en; // Enable CPU code execution (asynchronous) +input cpuoff; // Turns off the CPU +input dbg_cpu_reset;// Reset CPU from debug interface +input dbg_en; // Debug interface enable (asynchronous) +input dco_clk; // Fast oscillator (fast clock) +input lfxt_clk; // Low frequency oscillator (typ 32kHz) +input mclk_enable; // Main System Clock enable +input mclk_wkup; // Main System Clock wake-up (asynchronous) +input oscoff; // Turns off LFXT1 clock input +input [13:0] per_addr; // Peripheral address +input [15:0] per_din; // Peripheral data input +input per_en; // Peripheral enable (high active) +input [1:0] per_we; // Peripheral write enable (high active) +input reset_n; // Reset Pin (low active, asynchronous) +input scan_enable; // Scan enable (active during scan shifting) +input scan_mode; // Scan mode +input scg0; // System clock generator 1. Turns off the DCO +input scg1; // System clock generator 1. Turns off the SMCLK +input wdt_reset; // Watchdog-timer reset + + +//============================================================================= +// 1) WIRES & PARAMETER DECLARATION +//============================================================================= + +// Register base address (must be aligned to decoder bit width) +parameter [14:0] BASE_ADDR = 15'h0050; + +// Decoder bit width (defines how many bits are considered for address decoding) +parameter DEC_WD = 4; + +// Register addresses offset +parameter [DEC_WD-1:0] BCSCTL1 = 'h7, + BCSCTL2 = 'h8; + +// Register one-hot decoder utilities +parameter DEC_SZ = (1 << DEC_WD); +parameter [DEC_SZ-1:0] BASE_REG = {{DEC_SZ-1{1'b0}}, 1'b1}; + +// Register one-hot decoder +parameter [DEC_SZ-1:0] BCSCTL1_D = (BASE_REG << BCSCTL1), + BCSCTL2_D = (BASE_REG << BCSCTL2); + +// Local wire declarations +wire nodiv_mclk; +wire nodiv_mclk_n; +wire nodiv_smclk; + + +//============================================================================ +// 2) REGISTER DECODER +//============================================================================ + +// Local register selection +wire reg_sel = per_en & (per_addr[13:DEC_WD-1]==BASE_ADDR[14:DEC_WD]); + +// Register local address +wire [DEC_WD-1:0] reg_addr = {1'b0, per_addr[DEC_WD-2:0]}; + +// Register address decode +wire [DEC_SZ-1:0] reg_dec = (BCSCTL1_D & {DEC_SZ{(reg_addr==(BCSCTL1 >>1))}}) | + (BCSCTL2_D & {DEC_SZ{(reg_addr==(BCSCTL2 >>1))}}); + +// Read/Write probes +wire reg_lo_write = per_we[0] & reg_sel; +wire reg_hi_write = per_we[1] & reg_sel; +wire reg_read = ~|per_we & reg_sel; + +// Read/Write vectors +wire [DEC_SZ-1:0] reg_hi_wr = reg_dec & {DEC_SZ{reg_hi_write}}; +wire [DEC_SZ-1:0] reg_lo_wr = reg_dec & {DEC_SZ{reg_lo_write}}; +wire [DEC_SZ-1:0] reg_rd = reg_dec & {DEC_SZ{reg_read}}; + + +//============================================================================ +// 3) REGISTERS +//============================================================================ + +// BCSCTL1 Register +//-------------- +reg [7:0] bcsctl1; +wire bcsctl1_wr = BCSCTL1[0] ? reg_hi_wr[BCSCTL1] : reg_lo_wr[BCSCTL1]; +wire [7:0] bcsctl1_nxt = BCSCTL1[0] ? per_din[15:8] : per_din[7:0]; + +`ifdef ASIC + `ifdef ACLK_DIVIDER +wire [7:0] divax_mask = 8'h30; + `else +wire [7:0] divax_mask = 8'h00; + `endif +`else +wire [7:0] divax_mask = 8'h30; +`endif + +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) bcsctl1 <= 8'h00; + else if (bcsctl1_wr) bcsctl1 <= bcsctl1_nxt & divax_mask; // Mask unused bits + + +// BCSCTL2 Register +//-------------- +reg [7:0] bcsctl2; +wire bcsctl2_wr = BCSCTL2[0] ? reg_hi_wr[BCSCTL2] : reg_lo_wr[BCSCTL2]; +wire [7:0] bcsctl2_nxt = BCSCTL2[0] ? per_din[15:8] : per_din[7:0]; + +`ifdef MCLK_MUX +wire [7:0] selmx_mask = 8'h80; +`else +wire [7:0] selmx_mask = 8'h00; +`endif +`ifdef MCLK_DIVIDER +wire [7:0] divmx_mask = 8'h30; +`else +wire [7:0] divmx_mask = 8'h00; +`endif +`ifdef ASIC + `ifdef SMCLK_MUX +wire [7:0] sels_mask = 8'h08; + `else +wire [7:0] sels_mask = 8'h00; + `endif + `ifdef SMCLK_DIVIDER +wire [7:0] divsx_mask = 8'h06; + `else +wire [7:0] divsx_mask = 8'h00; + `endif +`else +wire [7:0] sels_mask = 8'h08; +wire [7:0] divsx_mask = 8'h06; +`endif + +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) bcsctl2 <= 8'h00; + else if (bcsctl2_wr) bcsctl2 <= bcsctl2_nxt & ( sels_mask | divsx_mask | + selmx_mask | divmx_mask); // Mask unused bits + + +//============================================================================ +// 4) DATA OUTPUT GENERATION +//============================================================================ + +// Data output mux +wire [15:0] bcsctl1_rd = {8'h00, (bcsctl1 & {8{reg_rd[BCSCTL1]}})} << (8 & {4{BCSCTL1[0]}}); +wire [15:0] bcsctl2_rd = {8'h00, (bcsctl2 & {8{reg_rd[BCSCTL2]}})} << (8 & {4{BCSCTL2[0]}}); + +wire [15:0] per_dout = bcsctl1_rd | + bcsctl2_rd; + + +//============================================================================= +// 5) DCO_CLK / LFXT_CLK INTERFACES (WAKEUP, ENABLE, ...) +//============================================================================= + +`ifdef ASIC + wire cpuoff_and_mclk_enable; + omsp_and_gate and_cpuoff_mclk_en (.y(cpuoff_and_mclk_enable), .a(cpuoff), .b(mclk_enable)); +`endif + +//----------------------------------------------------------- +// 5.1) HIGH SPEED SYSTEM CLOCK GENERATOR (DCO_CLK) +//----------------------------------------------------------- +// Note1: switching off the DCO osillator is only +// supported in ASIC mode with SCG0 low power mode +// +// Note2: unlike the original MSP430 specification, +// we allow to switch off the DCO even +// if it is selected by MCLK or SMCLK. + +wire por_a; +wire dco_wkup; +wire cpu_en_wkup; + +`ifdef SCG0_EN + + // The DCO oscillator is synchronously disabled if: + // - the cpu pin is disabled (in that case, wait for mclk_enable==0) + // - the debug interface is disabled + // - SCG0 is set (in that case, wait for the mclk_enable==0 if selected by SELMx) + // + // Note that we make extensive use of the AND gate module in order + // to prevent glitch propagation on the wakeup logic cone. + wire cpu_enabled_with_dco; + wire dco_not_enabled_by_dbg; + wire dco_disable_by_scg0; + wire dco_disable_by_cpu_en; + wire dco_enable_nxt; + omsp_and_gate and_dco_dis1 (.y(cpu_enabled_with_dco), .a(~bcsctl2[`SELMx]), .b(cpuoff_and_mclk_enable)); + omsp_and_gate and_dco_dis2 (.y(dco_not_enabled_by_dbg), .a(~dbg_en_s), .b(~cpu_enabled_with_dco)); + omsp_and_gate and_dco_dis3 (.y(dco_disable_by_scg0), .a(scg0), .b(dco_not_enabled_by_dbg)); + omsp_and_gate and_dco_dis4 (.y(dco_disable_by_cpu_en), .a(~cpu_en_s), .b(~mclk_enable)); + omsp_and_gate and_dco_dis5 (.y(dco_enable_nxt), .a(~dco_disable_by_scg0), .b(~dco_disable_by_cpu_en)); + + // Register to prevent glitch propagation + reg dco_disable; + always @(posedge nodiv_mclk_n or posedge por) + if (por) dco_disable <= 1'b1; + else dco_disable <= ~dco_enable_nxt; + + // Note that a synchronizer is required if the MCLK mux is included + wire dco_clk_n = ~dco_clk; + `ifdef MCLK_MUX + omsp_sync_cell sync_cell_dco_disable ( + .data_out (dco_enable), + .data_in (~dco_disable), + .clk (dco_clk_n), + .rst (por) + ); + `else + + assign dco_enable = ~dco_disable; + `endif + + // The DCO oscillator will get an asynchronous wakeup if: + // - the MCLK generates a wakeup (only if the MCLK mux selects dco_clk) + // - if the DCO wants to be synchronously enabled (i.e dco_enable_nxt=1) + wire dco_mclk_wkup; + wire dco_en_wkup; + omsp_and_gate and_dco_mclk_wkup (.y(dco_mclk_wkup), .a(mclk_wkup), .b(~bcsctl2[`SELMx])); + omsp_and_gate and_dco_en_wkup (.y(dco_en_wkup), .a(~dco_enable), .b(dco_enable_nxt)); + + wire dco_wkup_set = dco_mclk_wkup | dco_en_wkup | cpu_en_wkup; + + // Scan MUX for the asynchronous SET + wire dco_wkup_set_scan; + omsp_scan_mux scan_mux_dco_wkup ( + .scan_mode (scan_mode), + .data_in_scan (por_a), + .data_in_func (dco_wkup_set | por), + .data_out (dco_wkup_set_scan) + ); + + // Scan MUX to increase coverage + wire dco_wkup_clear; + omsp_scan_mux scan_mux_dco_wkup_clear ( + .scan_mode (scan_mode), + .data_in_scan (dco_wkup_set), + .data_in_func (1'b1), + .data_out (dco_wkup_clear) + ); + + // The wakeup is asynchronously set, synchronously released + wire dco_wkup_n; + omsp_sync_cell sync_cell_dco_wkup ( + .data_out (dco_wkup_n), + .data_in (dco_wkup_clear), + .clk (dco_clk_n), + .rst (dco_wkup_set_scan) + ); + + omsp_and_gate and_dco_wkup (.y(dco_wkup), .a(~dco_wkup_n), .b(cpu_en)); + +`else + assign dco_enable = 1'b1; + assign dco_wkup = 1'b1; +`endif + + +//----------------------------------------------------------- +// 5.2) LOW FREQUENCY CRYSTAL CLOCK GENERATOR (LFXT_CLK) +//----------------------------------------------------------- + +// ASIC MODE +//------------------------------------------------ +// Note: unlike the original MSP430 specification, +// we allow to switch off the LFXT even +// if it is selected by MCLK or SMCLK. +`ifdef ASIC + +`ifdef OSCOFF_EN + + // The LFXT is synchronously disabled if: + // - the cpu pin is disabled (in that case, wait for mclk_enable==0) + // - the debug interface is disabled + // - OSCOFF is set (in that case, wait for the mclk_enable==0 if selected by SELMx) + wire cpu_enabled_with_lfxt; + wire lfxt_not_enabled_by_dbg; + wire lfxt_disable_by_oscoff; + wire lfxt_disable_by_cpu_en; + wire lfxt_enable_nxt; + omsp_and_gate and_lfxt_dis1 (.y(cpu_enabled_with_lfxt), .a(bcsctl2[`SELMx]), .b(cpuoff_and_mclk_enable)); + omsp_and_gate and_lfxt_dis2 (.y(lfxt_not_enabled_by_dbg), .a(~dbg_en_s), .b(~cpu_enabled_with_lfxt)); + omsp_and_gate and_lfxt_dis3 (.y(lfxt_disable_by_oscoff), .a(oscoff), .b(lfxt_not_enabled_by_dbg)); + omsp_and_gate and_lfxt_dis4 (.y(lfxt_disable_by_cpu_en), .a(~cpu_en_s), .b(~mclk_enable)); + omsp_and_gate and_lfxt_dis5 (.y(lfxt_enable_nxt), .a(~lfxt_disable_by_oscoff), .b(~lfxt_disable_by_cpu_en)); + + // Register to prevent glitch propagation + reg lfxt_disable; + always @(posedge nodiv_mclk_n or posedge por) + if (por) lfxt_disable <= 1'b1; + else lfxt_disable <= ~lfxt_enable_nxt; + + // Synchronize the OSCOFF control signal to the LFXT clock domain + wire lfxt_clk_n = ~lfxt_clk; + omsp_sync_cell sync_cell_lfxt_disable ( + .data_out (lfxt_enable), + .data_in (~lfxt_disable), + .clk (lfxt_clk_n), + .rst (por) + ); + + // The LFXT will get an asynchronous wakeup if: + // - the MCLK generates a wakeup (only if the MCLK mux selects lfxt_clk) + // - if the LFXT wants to be synchronously enabled (i.e lfxt_enable_nxt=1) + wire lfxt_mclk_wkup; + wire lfxt_en_wkup; + omsp_and_gate and_lfxt_mclk_wkup (.y(lfxt_mclk_wkup), .a(mclk_wkup), .b(bcsctl2[`SELMx])); + omsp_and_gate and_lfxt_en_wkup (.y(lfxt_en_wkup), .a(~lfxt_enable), .b(lfxt_enable_nxt)); + + wire lfxt_wkup_set = lfxt_mclk_wkup | lfxt_en_wkup | cpu_en_wkup; + + // Scan MUX for the asynchronous SET + wire lfxt_wkup_set_scan; + omsp_scan_mux scan_mux_lfxt_wkup ( + .scan_mode (scan_mode), + .data_in_scan (por_a), + .data_in_func (lfxt_wkup_set | por), + .data_out (lfxt_wkup_set_scan) + ); + + // Scan MUX to increase coverage + wire lfxt_wkup_clear; + omsp_scan_mux scan_mux_lfxt_wkup_clear ( + .scan_mode (scan_mode), + .data_in_scan (lfxt_wkup_set), + .data_in_func (1'b1), + .data_out (lfxt_wkup_clear) + ); + + // The wakeup is asynchronously set, synchronously released + wire lfxt_wkup_n; + omsp_sync_cell sync_cell_lfxt_wkup ( + .data_out (lfxt_wkup_n), + .data_in (lfxt_wkup_clear), + .clk (lfxt_clk_n), + .rst (lfxt_wkup_set_scan) + ); + + omsp_and_gate and_lfxt_wkup (.y(lfxt_wkup), .a(~lfxt_wkup_n), .b(cpu_en)); + +`else + assign lfxt_enable = 1'b1; + assign lfxt_wkup = 1'b0; +`endif + + +// FPGA MODE +//--------------------------------------- +// Synchronize LFXT_CLK & edge detection +`else + +wire lfxt_clk_s; + +omsp_sync_cell sync_cell_lfxt_clk ( + .data_out (lfxt_clk_s), + .data_in (lfxt_clk), + .clk (mclk), + .rst (por) +); + +reg lfxt_clk_dly; + +always @ (posedge mclk or posedge por) + if (por) lfxt_clk_dly <= 1'b0; + else lfxt_clk_dly <= lfxt_clk_s; + +wire lfxt_clk_en = (lfxt_clk_s & ~lfxt_clk_dly) & ~(oscoff & ~bcsctl2[`SELS]); +assign lfxt_enable = 1'b1; +assign lfxt_wkup = 1'b0; +`endif + + +//============================================================================= +// 6) CLOCK GENERATION +//============================================================================= + +//----------------------------------------------------------- +// 6.1) GLOBAL CPU ENABLE +//----------------------------------------------------------- +// ACLK and SMCLK are directly switched-off +// with the cpu_en pin (after synchronization). +// MCLK will be switched off once the CPU reaches +// its IDLE state (through the mclk_enable signal) + + +// Synchronize CPU_EN signal to the MCLK domain +//---------------------------------------------- +`ifdef SYNC_CPU_EN + omsp_sync_cell sync_cell_cpu_en ( + .data_out (cpu_en_s), + .data_in (cpu_en), + .clk (nodiv_mclk), + .rst (por) + ); + omsp_and_gate and_cpu_en_wkup (.y(cpu_en_wkup), .a(cpu_en), .b(~cpu_en_s)); +`else + assign cpu_en_s = cpu_en; + assign cpu_en_wkup = 1'b0; +`endif + +// Synchronize CPU_EN signal to the ACLK domain +//---------------------------------------------- +`ifdef LFXT_DOMAIN + wire cpu_en_aux_s; + omsp_sync_cell sync_cell_cpu_aux_en ( + .data_out (cpu_en_aux_s), + .data_in (cpu_en), + .clk (lfxt_clk), + .rst (por) + ); +`else + wire cpu_en_aux_s = cpu_en_s; +`endif + +// Synchronize CPU_EN signal to the SMCLK domain +//---------------------------------------------- +// Note: the synchronizer is only required if there is a SMCLK_MUX +`ifdef ASIC + `ifdef SMCLK_MUX + wire cpu_en_sm_s; + omsp_sync_cell sync_cell_cpu_sm_en ( + .data_out (cpu_en_sm_s), + .data_in (cpu_en), + .clk (nodiv_smclk), + .rst (por) + ); + `else + wire cpu_en_sm_s = cpu_en_s; + `endif +`endif + + +//----------------------------------------------------------- +// 6.2) MCLK GENERATION +//----------------------------------------------------------- + +// Clock MUX +//---------------------------- +`ifdef MCLK_MUX +omsp_clock_mux clock_mux_mclk ( + .clk_out (nodiv_mclk), + .clk_in0 (dco_clk), + .clk_in1 (lfxt_clk), + .reset (por), + .scan_mode (scan_mode), + .select (bcsctl2[`SELMx]) +); +`else +assign nodiv_mclk = dco_clk; +`endif +assign nodiv_mclk_n = ~nodiv_mclk; + + +// Wakeup synchronizer +//---------------------------- +wire mclk_wkup_s; + +`ifdef CPUOFF_EN +omsp_sync_cell sync_cell_mclk_wkup ( + .data_out (mclk_wkup_s), + .data_in (mclk_wkup), + .clk (nodiv_mclk), + .rst (puc_rst) +); +`else + assign mclk_wkup_s = 1'b0; +`endif + + +// Clock Divider +//---------------------------- +// No need for extra synchronizer as bcsctl2 +// comes from the same clock domain. + +`ifdef CPUOFF_EN +wire mclk_active = mclk_enable | mclk_wkup_s | (dbg_en_s & cpu_en_s); +`else +wire mclk_active = 1'b1; +`endif + +`ifdef MCLK_DIVIDER +reg [2:0] mclk_div; +always @ (posedge nodiv_mclk or posedge puc_rst) + if (puc_rst) mclk_div <= 3'h0; + else if ((bcsctl2[`DIVMx]!=2'b00)) mclk_div <= mclk_div+3'h1; + + wire mclk_div_en = mclk_active & ((bcsctl2[`DIVMx]==2'b00) ? 1'b1 : + (bcsctl2[`DIVMx]==2'b01) ? mclk_div[0] : + (bcsctl2[`DIVMx]==2'b10) ? &mclk_div[1:0] : + &mclk_div[2:0]); +`else + wire mclk_div_en = mclk_active; +`endif + + +// Generate main system clock +//---------------------------- +`ifdef MCLK_CGATE + +omsp_clock_gate clock_gate_mclk ( + .gclk (mclk), + .clk (nodiv_mclk), + .enable (mclk_div_en), + .scan_enable (scan_enable) +); +`else + assign mclk = nodiv_mclk; +`endif + + +//----------------------------------------------------------- +// 6.3) ACLK GENERATION +//----------------------------------------------------------- + +// ASIC MODE +//---------------------------- +`ifdef ASIC + + `ifdef ACLK_DIVIDER + `ifdef LFXT_DOMAIN + + wire nodiv_aclk = lfxt_clk; + + // Local Reset synchronizer + wire puc_lfxt_rst; + wire puc_lfxt_noscan_n; + omsp_sync_cell sync_cell_puc_lfxt ( + .data_out (puc_lfxt_noscan_n), + .data_in (1'b1), + .clk (nodiv_aclk), + .rst (puc_rst) + ); + omsp_scan_mux scan_mux_puc_lfxt ( + .scan_mode (scan_mode), + .data_in_scan (por_a), + .data_in_func (~puc_lfxt_noscan_n), + .data_out (puc_lfxt_rst) + ); + + // Local synchronizer for the bcsctl1.DIVAx configuration + // (note that we can live with a full bus synchronizer as + // it won't hurt if we get a wrong DIVAx value for a single clock cycle) + reg [1:0] divax_s; + reg [1:0] divax_ss; + always @ (posedge nodiv_aclk or posedge puc_lfxt_rst) + if (puc_lfxt_rst) + begin + divax_s <= 2'h0; + divax_ss <= 2'h0; + end + else + begin + divax_s <= bcsctl1[`DIVAx]; + divax_ss <= divax_s; + end + + // If the OSCOFF mode is enabled synchronize OSCOFF signal + wire oscoff_s; + `ifdef OSCOFF_EN + omsp_sync_cell sync_cell_oscoff ( + .data_out (oscoff_s), + .data_in (oscoff), + .clk (nodiv_aclk), + .rst (puc_lfxt_rst) + ); + `else + assign oscoff_s = 1'b0; + `endif + `else + wire puc_lfxt_rst = puc_rst; + wire nodiv_aclk = dco_clk; + wire [1:0] divax_ss = bcsctl1[`DIVAx]; + wire oscoff_s = oscoff; + `endif + + // Divider + reg [2:0] aclk_div; + always @ (posedge nodiv_aclk or posedge puc_lfxt_rst) + if (puc_lfxt_rst) aclk_div <= 3'h0; + else if ((divax_ss!=2'b00)) aclk_div <= aclk_div+3'h1; + + wire aclk_div_en = cpu_en_aux_s & ~oscoff_s & ((divax_ss==2'b00) ? 1'b1 : + (divax_ss==2'b01) ? aclk_div[0] : + (divax_ss==2'b10) ? &aclk_div[1:0] : + &aclk_div[2:0]); + + // Clock gate + omsp_clock_gate clock_gate_aclk ( + .gclk (aclk), + .clk (nodiv_aclk), + .enable (aclk_div_en), + .scan_enable (scan_enable) + ); + + `else + `ifdef LFXT_DOMAIN + assign aclk = lfxt_clk; + `else + assign aclk = dco_clk; + `endif + `endif + + + assign aclk_en = 1'b1; + + +// FPGA MODE +//---------------------------- +`else + reg aclk_en; + reg [2:0] aclk_div; + wire aclk_en_nxt = lfxt_clk_en & ((bcsctl1[`DIVAx]==2'b00) ? 1'b1 : + (bcsctl1[`DIVAx]==2'b01) ? aclk_div[0] : + (bcsctl1[`DIVAx]==2'b10) ? &aclk_div[1:0] : + &aclk_div[2:0]); + + always @ (posedge mclk or posedge puc_rst) + if (puc_rst) aclk_div <= 3'h0; + else if ((bcsctl1[`DIVAx]!=2'b00) & lfxt_clk_en) aclk_div <= aclk_div+3'h1; + + always @ (posedge mclk or posedge puc_rst) + if (puc_rst) aclk_en <= 1'b0; + else aclk_en <= aclk_en_nxt & cpu_en_s; + + assign aclk = mclk; +`endif + +//----------------------------------------------------------- +// 6.4) SMCLK GENERATION +//----------------------------------------------------------- + +// Clock MUX +//---------------------------- +`ifdef SMCLK_MUX +omsp_clock_mux clock_mux_smclk ( + .clk_out (nodiv_smclk), + .clk_in0 (dco_clk), + .clk_in1 (lfxt_clk), + .reset (por), + .scan_mode (scan_mode), + .select (bcsctl2[`SELS]) +); +`else +assign nodiv_smclk = dco_clk; +`endif + + +// ASIC MODE +//---------------------------- +`ifdef ASIC + `ifdef SMCLK_MUX + + // Synchronizers + //------------------------------------------------------ + // When the SMCLK MUX is enabled, the reset and DIVSx + // and SCG1 signals must be synchronized, otherwise not. + + // Local Reset synchronizer + wire puc_sm_noscan_n; + wire puc_sm_rst; + omsp_sync_cell sync_cell_puc_sm ( + .data_out (puc_sm_noscan_n), + .data_in (1'b1), + .clk (nodiv_smclk), + .rst (puc_rst) + ); + omsp_scan_mux scan_mux_puc_sm ( + .scan_mode (scan_mode), + .data_in_scan (por_a), + .data_in_func (~puc_sm_noscan_n), + .data_out (puc_sm_rst) + ); + + // SCG1 synchronizer + `ifdef SCG1_EN + wire scg1_s; + omsp_sync_cell sync_cell_scg1 ( + .data_out (scg1_s), + .data_in (scg1), + .clk (nodiv_smclk), + .rst (puc_sm_rst) + ); + `else + wire scg1_s = 1'b0; + `endif + + `ifdef SMCLK_DIVIDER + // Local synchronizer for the bcsctl2.DIVSx configuration + // (note that we can live with a full bus synchronizer as + // it won't hurt if we get a wrong DIVSx value for a single clock cycle) + reg [1:0] divsx_s; + reg [1:0] divsx_ss; + always @ (posedge nodiv_smclk or posedge puc_sm_rst) + if (puc_sm_rst) + begin + divsx_s <= 2'h0; + divsx_ss <= 2'h0; + end + else + begin + divsx_s <= bcsctl2[`DIVSx]; + divsx_ss <= divsx_s; + end + `endif + + `else + + wire puc_sm_rst = puc_rst; + wire [1:0] divsx_ss = bcsctl2[`DIVSx]; + wire scg1_s = scg1; + `endif + + + + // Clock Divider + //---------------------------- + `ifdef SMCLK_DIVIDER + + reg [2:0] smclk_div; + always @ (posedge nodiv_smclk or posedge puc_sm_rst) + if (puc_sm_rst) smclk_div <= 3'h0; + else if ((divsx_ss!=2'b00)) smclk_div <= smclk_div+3'h1; + + wire smclk_div_en = cpu_en_sm_s & ~scg1_s & ((divsx_ss==2'b00) ? 1'b1 : + (divsx_ss==2'b01) ? smclk_div[0] : + (divsx_ss==2'b10) ? &smclk_div[1:0] : + &smclk_div[2:0]); + `else + `ifdef SCG1_EN + wire smclk_div_en = cpu_en_sm_s & ~scg1_s; + `else + wire smclk_div_en = cpu_en_sm_s; + `endif + `endif + + + // Generate sub-system clock + //---------------------------- + `ifdef SMCLK_CGATE + omsp_clock_gate clock_gate_smclk ( + .gclk (smclk), + .clk (nodiv_smclk), + .enable (smclk_div_en), + .scan_enable (scan_enable) + ); + `else + assign smclk = nodiv_smclk; + `endif + + assign smclk_en = 1'b1; + + +// FPGA MODE +//---------------------------- +`else +reg smclk_en; +reg [2:0] smclk_div; + +wire smclk_in = ~scg1 & (bcsctl2[`SELS] ? lfxt_clk_en : 1'b1); + +wire smclk_en_nxt = smclk_in & ((bcsctl2[`DIVSx]==2'b00) ? 1'b1 : + (bcsctl2[`DIVSx]==2'b01) ? smclk_div[0] : + (bcsctl2[`DIVSx]==2'b10) ? &smclk_div[1:0] : + &smclk_div[2:0]); + +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) smclk_en <= 1'b0; + else smclk_en <= smclk_en_nxt & cpu_en_s; + +always @ (posedge mclk or posedge puc_rst) + if (puc_rst) smclk_div <= 3'h0; + else if ((bcsctl2[`DIVSx]!=2'b00) & smclk_in) smclk_div <= smclk_div+3'h1; + +wire smclk = mclk; + +`endif + +//----------------------------------------------------------- +// 6.5) DEBUG INTERFACE CLOCK GENERATION (DBG_CLK) +//----------------------------------------------------------- + +// Synchronize DBG_EN signal to MCLK domain +//------------------------------------------ +`ifdef DBG_EN +`ifdef SYNC_DBG_EN + wire dbg_en_n_s; + omsp_sync_cell sync_cell_dbg_en ( + .data_out (dbg_en_n_s), + .data_in (~dbg_en), + .clk (mclk), + .rst (por) + ); + assign dbg_en_s = ~dbg_en_n_s; + wire dbg_rst_nxt = dbg_en_n_s; +`else + assign dbg_en_s = dbg_en; + wire dbg_rst_nxt = ~dbg_en; +`endif +`else + assign dbg_en_s = 1'b0; + wire dbg_rst_nxt = 1'b0; +`endif + + +// Serial Debug Interface Clock gate +//------------------------------------------------ +`ifdef DBG_EN + `ifdef ASIC + omsp_clock_gate clock_gate_dbg_clk ( + .gclk (dbg_clk), + .clk (mclk), + .enable (dbg_en_s), + .scan_enable (scan_enable) + ); + `else + assign dbg_clk = dco_clk; + `endif +`else + assign dbg_clk = 1'b0; +`endif + + +//============================================================================= +// 7) RESET GENERATION +//============================================================================= +// +// Whenever the reset pin (reset_n) is deasserted, the internal resets of the +// openMSP430 will be released in the following order: +// 1- POR +// 2- DBG_RST (if the sdi interface is enabled, i.e. dbg_en=1) +// 3- PUC +// +// Note: releasing the DBG_RST before PUC is particularly important in order +// to allow the sdi interface to halt the cpu immediately after a PUC. +// + +// Generate synchronized POR to MCLK domain +//------------------------------------------ + +// Asynchronous reset source +assign por_a = !reset_n; +wire por_noscan; + +// Reset Synchronizer +omsp_sync_reset sync_reset_por ( + .rst_s (por_noscan), + .clk (nodiv_mclk), + .rst_a (por_a) +); + +// Scan Reset Mux +`ifdef ASIC +omsp_scan_mux scan_mux_por ( + .scan_mode (scan_mode), + .data_in_scan (por_a), + .data_in_func (por_noscan), + .data_out (por) +); +`else + assign por = por_noscan; +`endif + +// Generate synchronized reset for the SDI +//------------------------------------------ +`ifdef DBG_EN + +// Reset Generation +reg dbg_rst_noscan; +always @ (posedge mclk or posedge por) + if (por) dbg_rst_noscan <= 1'b1; + else dbg_rst_noscan <= dbg_rst_nxt; + + // Scan Reset Mux + `ifdef ASIC + omsp_scan_mux scan_mux_dbg_rst ( + .scan_mode (scan_mode), + .data_in_scan (por_a), + .data_in_func (dbg_rst_noscan), + .data_out (dbg_rst) + ); + `else + assign dbg_rst = dbg_rst_noscan; + `endif + +`else + wire dbg_rst_noscan = 1'b1; + assign dbg_rst = 1'b1; +`endif + + +// Generate main system reset (PUC_RST) +//-------------------------------------- +wire puc_noscan_n; +wire puc_a_scan; + +// Asynchronous PUC reset +wire puc_a = por | wdt_reset; + +// Synchronous PUC reset +wire puc_s = dbg_cpu_reset | // With the debug interface command + + (dbg_en_s & dbg_rst_noscan & ~puc_noscan_n); // Sequencing making sure PUC is released + // after DBG_RST if the debug interface is + // enabled at power-on-reset time +// Scan Reset Mux +`ifdef ASIC +omsp_scan_mux scan_mux_puc_rst_a ( + .scan_mode (scan_mode), + .data_in_scan (por_a), + .data_in_func (puc_a), + .data_out (puc_a_scan) +); +`else + assign puc_a_scan = puc_a; +`endif + +// Reset Synchronizer +// (required because of the asynchronous watchdog reset) +omsp_sync_cell sync_cell_puc ( + .data_out (puc_noscan_n), + .data_in (~puc_s), + .clk (mclk), + .rst (puc_a_scan) +); + +// Scan Reset Mux +`ifdef ASIC +omsp_scan_mux scan_mux_puc_rst ( + .scan_mode (scan_mode), + .data_in_scan (por_a), + .data_in_func (~puc_noscan_n), + .data_out (puc_rst) +); +`else + assign puc_rst = ~puc_noscan_n; +`endif + +// PUC pending set the serial debug interface +assign puc_pnd_set = ~puc_noscan_n; + + +endmodule // omsp_clock_module + +`ifdef OMSP_NO_INCLUDE +`else +`include "openMSP430_undefines.v" +`endif |