Документ взят из кэша поисковой машины. Адрес оригинального документа : http://www.naic.edu/~phil/hardware/pdev/fpga/gx/plinth/src/pi.v
Дата изменения: Thu Jun 26 04:26:02 2008
Дата индексирования: Sat Sep 6 19:56:58 2008
Кодировка:

Поисковые слова: http astrokuban.info astrokuban

// Jeff Mock
// 2030 Gough St.
// San Francisco, CA 94109
// jeff@mock.com
//
// Copyright 2005,2006
//
// $URL: https://www.mock.com/svn/pdev/trunk/gx/plinth/src/pi.v $
// $Id: pi.v 876 2007-02-04 05:03:04Z jeff $

module pi (
ck,
master,

ps_clk,
ps_incdec,
ps_en,

pi_ck,
pi_resetb,
pi_addr,
pi_data,
pi_par,
pi_csb,
pi_rd,
pi_oeb,
pi_web,
pi_lastb,
pi_ready,
pi_err,
pi_dmareq,
// pi_dmaack,
// pi_dmaeot,
pi_int,

pfo_re,
pfo_ready,
pfo_dat,
pfo_datp,
pfo_vld,

ctl_we,
ctl_addr,
ctl_data,
ctl_rd_data,

preg_pfifo_len,
preg_ramcfg,
preg_packdiag,
preg_swap,
sreg_pfifo_over,
sreg_pfifo_under,
sreg_pmax,
sp_id,
board_rev,
adc_rev,
adcclk_sel,
adcclk_en,
qdr_k_en,
qdr_c_en,

gp_oeb,
gp,
preg_gpinv,
preg_gpsel,
preg_gpout,
preg_gpintsel,
preg_gpforce,
preg_adc0_off,
preg_adc1_off,
preg_adc2_off,
preg_adc3_off,
preg_adc0_scale,
preg_adc1_scale,
preg_adc2_scale,
preg_adc3_scale,

reset,
reset_pfifo,
adc_dcm_reset,
adc_dcmret_reset,

obs_start,
obs_stop,

keep
);

input ck;
input master;

output ps_clk;
output ps_incdec;
output ps_en;

// powerpc peripheral bus interface
//
input pi_ck;
input pi_resetb;
input [23:0] pi_addr;
inout [31:0] pi_data;
inout [3:0] pi_par;
input [7:1] pi_csb;
input pi_rd;
input pi_oeb;
input pi_web;
input pi_lastb;
output pi_ready;
output pi_err;
output [1:0] pi_dmareq;
// input [1:0] pi_dmaack;
// input [1:0] pi_dmaeot;
output [3:0] pi_int;

// data from output side of external sram fifo in ck clock
// domain
output pfo_re;
input pfo_ready;
input [63:0] pfo_dat;
input [7:0] pfo_datp;
input pfo_vld;

output ctl_we;
output [15:0] ctl_addr;
output [15:0] ctl_data;
input [15:0] ctl_rd_data;

output [19:0] preg_pfifo_len;
output [7:0] preg_ramcfg;
output [15:0] preg_packdiag;
output [2:0] preg_swap;
input sreg_pfifo_over;
input sreg_pfifo_under;
input [15:0] sreg_pmax;
input [15:0] sp_id;
inout [3:0] board_rev;
inout [7:0] adc_rev;
output adcclk_sel;
output adcclk_en;
output qdr_k_en;
output qdr_c_en;

output [`GP_IN-1:0] gp_oeb;
input [`GP_IN-1:0] gp;
output [`GP_IN-1:0] preg_gpinv;
output [`GP_IN-1:0] preg_gpsel;
output [`GP_IN-1:0] preg_gpout;
output [`GP_IN-1:0] preg_gpintsel;
output [`GP_IN-1:0] preg_gpforce;
output [`N_ADC-1:0] preg_adc0_off;
output [`N_ADC-1:0] preg_adc1_off;
output [`N_ADC-1:0] preg_adc2_off;
output [`N_ADC-1:0] preg_adc3_off;
output [15:0] preg_adc0_scale;
output [15:0] preg_adc1_scale;
output [15:0] preg_adc2_scale;
output [15:0] preg_adc3_scale;

output reset;
output reset_pfifo;
output adc_dcm_reset;
output adc_dcmret_reset;

output obs_start;
output obs_stop;

output keep;

// Local preg_ registers control fifos and DMA.
//
reg [15:0] preg_test;
reg [15:0] preg_test2;
reg [4:0] preg_pfifo_lwm;
reg [4:0] preg_pfifo_hwm;
reg [3:0] preg_dma_bl;
reg [1:0] preg_dma_chan;
reg [19:0] preg_pfifo_len_pck;
reg [7:0] preg_ramcfg_pck;
reg [15:0] preg_packdiag_pck;
reg preg_reset;
reg preg_reset_pfifo;
reg [2:0] preg_swap_pck;
reg [15:0] preg_clkctl;
reg [`GP_IN-1:0] preg_gpoe;
reg [7:0] preg_pidiag_pck;
reg [`GP_IN-1:0] preg_gpsel_pck;
reg [`GP_IN-1:0] preg_gpout_pck;
reg [`GP_IN-1:0] preg_gpinv_pck;
reg [`GP_IN-1:0] preg_gpintsel_pck;
reg [`GP_IN-1:0] preg_gpforce_pck;
reg [3:0] preg_ppssel_pck;
reg preg_run_pck;
reg [`N_ADC-1:0] preg_adc0_off_pck;
reg [`N_ADC-1:0] preg_adc1_off_pck;
reg [`N_ADC-1:0] preg_adc2_off_pck;
reg [`N_ADC-1:0] preg_adc3_off_pck;
reg [15:0] preg_adc0_scale_pck;
reg [15:0] preg_adc1_scale_pck;
reg [15:0] preg_adc2_scale_pck;
reg [15:0] preg_adc3_scale_pck;

//
reg [19:0] preg_pfifo_len;
reg [7:0] preg_ramcfg;
reg [15:0] preg_packdiag;
reg [2:0] preg_swap;
reg [7:0] preg_pidiag;
reg [`GP_IN-1:0] preg_gpsel;
reg [`GP_IN-1:0] preg_gpout;
reg [`GP_IN-1:0] preg_gpinv;
reg [`GP_IN-1:0] preg_gpintsel;
reg [`GP_IN-1:0] preg_gpforce;
reg [3:0] preg_ppssel;
reg preg_run;
reg [`N_ADC-1:0] preg_adc0_off;
reg [`N_ADC-1:0] preg_adc1_off;
reg [`N_ADC-1:0] preg_adc2_off;
reg [`N_ADC-1:0] preg_adc3_off;
reg [15:0] preg_adc0_scale;
reg [15:0] preg_adc1_scale;
reg [15:0] preg_adc2_scale;
reg [15:0] preg_adc3_scale;

// DLL to align pck with external pi_ck
//
wire pi_ck_in;
wire pi_ck_dcm;
wire pck;
IBUFG pck_ibuf (
.I ( pi_ck ),
.O ( pi_ck_in )
);
DCM pck_dcm (
.CLK0 ( pi_ck_dcm ),
.RST ( 1'b0 ),
.CLKFB ( pck ),
.CLKIN ( pi_ck_in )
);
BUFG pck_bufg (
.I ( pi_ck_dcm ),
.O ( pck )
);

// Resync external reset
//
reg pi_resetb_d1;
reg pi_reset_d2;
reg preset;
always @(posedge pck) begin
pi_resetb_d1 <= pi_resetb;
pi_reset_d2 <= ~pi_resetb_d1;
preset <= pi_reset_d2;
end

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// Take pck domain reset to ck domain along with
// preg register to force reset of sp.
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
reg reset_p2;
reg reset_p1;
reg reset;
always @(posedge ck or posedge preset) begin
if (preset) begin
reset_p2 <= 1'b1;
reset_p1 <= 1'b1;
reset <= 1'b1;
end else begin
reset_p2 <= preset | preg_reset;
reset_p1 <= reset_p2;
reset <= reset_p1;
end
end

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// Generate two resets for clearing DMA related
// fifos. Generate one for each clock domain.
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
reg reset_pfifo_ck;
reg reset_pfifo_pck;
always @(posedge ck or posedge reset)
if (reset)
reset_pfifo_ck <= 1'b1;
else
reset_pfifo_ck <= preg_reset_pfifo;
always @(posedge pck)
reset_pfifo_pck <= preg_reset_pfifo | preset;
assign reset_pfifo = reset_pfifo_ck;

// Okay, measure clock period of ADC clock to a reasonable
// precision as a sanity check. pck is a known clock, most
// likely 66MHz or 83MHz. Measure ck counts with a fixed
// timing window of 16384-8 pck clocks.
//
reg [13:0] tm_wincnt;
reg tm_clear;
reg tm_latch;
reg tm_window;
always @(posedge pck) begin
if (preset) begin
tm_wincnt <= 14'd0;
tm_clear <= 1'b0;
tm_latch <= 1'b0;
tm_window <= 1'b0;
end else begin
tm_wincnt <= tm_wincnt + 1;
tm_clear <= (tm_wincnt == 14'h0000) | (tm_wincnt == 14'h0001);
tm_latch <= (tm_wincnt == 14'h3ffe);
tm_window <= (tm_wincnt == 14'h0004) ? 1'b1 :
(tm_wincnt == 14'h3ffb) ? 1'b0 : tm_window;
end
end

// Move tm_window to ck clock
reg tm_window_ck;
reg [15:0] ck_wcnt;
always @(posedge ck)
tm_window_ck <= tm_window;
always @(posedge ck or posedge tm_clear) begin
if (tm_clear)
ck_wcnt <= 16'd0;
else
ck_wcnt <= tm_window_ck ? (ck_wcnt + 15'd1) : ck_wcnt;
end

// Cross clock bounday from ck_gate to pck, latch count
// at end of window store in register so pi interface can
// read frequency.
reg [15:0] ck_wcnt_lat;
always @(posedge pck)
if (tm_latch)
ck_wcnt_lat <= ck_wcnt;


// Diagnostic interface to pi_fifo. Using prev_pidiag register
// a PRN is hooked up to the write side of the pi_fifo to
// test the async fifo and bus interface to the PPC.
//
wire pfox_ready;
reg pd_stdel;
reg pd_start;
reg pd_wr;
always @(posedge ck) begin
if (reset) begin
pd_stdel <= 1'b0;
pd_start <= 1'b0;
pd_wr <= 1'b0;
end else begin
pd_stdel <= preg_pidiag[0];
pd_start <= ~pd_stdel & preg_pidiag[0];
pd_wr <= preg_pidiag[0] & pd_stdel & ~pd_start & pfox_ready;
end
end
//
reg [31:0] pd_prn;
wire [3:0] pd_prn_p;
wire pd_prn_n;
assign pd_prn_n = ~(pd_prn[31] ^ pd_prn[1] ^ pd_prn[0]);
always @(posedge ck) begin
if (pd_start)
pd_prn <= 32'h01020304;
else if (pd_wr)
pd_prn <= { pd_prn_n, pd_prn[31:1] };
end
assign pd_prn_p = ~{ ^pd_prn[31:24], ^pd_prn[23:16],
^pd_prn[15:8], ^pd_prn[7:0] };


// Pull things out of the external sram fifo (pfo_*), this
// is 64-bits per entry. Put this into a smaller 32-bit by
// 32-entry fifo that cross the clock boundary from the ADC
// clock to the ppc bus clock.
//

// Ask for something from the pfo fifo if there is a
// valid entry and there is room to put it in the pi_fifo.
// Pull things out of the pack_fifo no faster than every
// other clock since we're narrowing to 32-bits.
//
reg pfo_re;
reg pfo_re_d1;
wire pfo_re2;
assign pfo_re2 = (~preg_pidiag[1] & (pfo_re | pfo_re_d1)) |
(preg_pidiag[1] & pd_wr);

always @(posedge ck) begin
pfo_re <= reset ? 1'b0 : pfox_ready & pfo_ready & ~pfo_re;
pfo_re_d1 <= pfo_re;
end

// When 64-bits comes back from the pack_fifo, put 32-bits
// into the pi_fifo and save 32-bits until the next clock
// cycle.
//
reg [31:0] pfo_dat_d1_l;
reg [3:0] pfo_datp_d1_l;
reg [31:0] pfox_dat;
reg [3:0] pfox_datp;
always @(posedge ck) begin
pfo_dat_d1_l <= pfo_dat[31:0];
pfo_datp_d1_l <= pfo_datp[3:0];
pfox_dat <= pd_wr ? pd_prn :
pfo_vld ? pfo_dat[63:32] : pfo_dat_d1_l[31:0];
pfox_datp <= pd_wr ? pd_prn_p :
pfo_vld ? pfo_datp[7:4] : pfo_datp_d1_l[3:0];
end

// Assemble a valid bit for the 32-bit data into the pi_fifo.
//
reg pfox_vld;
reg pfox_vld_d1;
always @(posedge ck) begin
pfox_vld_d1 <= pfo_vld;
pfox_vld <= (~preg_pidiag[1] & (pfo_vld | pfox_vld_d1)) |
(preg_pidiag[1] & pd_wr);
end


// Small FIFO for DMA of spectral data to powerpc. This
// FIFO is the clock boundary crossing from the ADC clock
// to the power pc bus clock.
//
// The latency for reading from the pack_fifo is quite
// high (maybe 8 cycles?), so this fifo has an extra
// early write pointer just used for calculating wr_oky
// (the ~almost_full bit). wr_early is asserted when the
// read request is made to the pack_fifo and is used by
// pi_fifo to calculate flow control.
//
// The actual write to the fifo is done when valid data
// arrives from the pack_fifo. This write maintains the
// "real" write pointer used for calculating rd_okay
// (~almost_empty) on the powerpc clock domain read side
// of the fifo.
//
wire [31:0] pif_dat;
wire [3:0] pif_datp;
wire pif_rd;
wire [1:0] pif_rd_early;
wire pif_okay;
wire sreg_pi_wr_under;
wire sreg_pi_wr_over;
wire sreg_pi_rd_under;
wire sreg_pi_rd_over;
wire [4:0] sreg_pi_rd_cnt;

pi_fifo pi_fifo (
.wr_ck ( ck ),
.wr_reset ( reset_pfifo_ck ),
.wr ( pfox_vld ),
.wr_early ( pfo_re2 ),
.wr_di ( pfox_dat ),
.wr_dip ( pfox_datp ),
.wr_okay ( pfox_ready ),
.wr_under ( sreg_pi_wr_under ),
.wr_over ( sreg_pi_wr_over ),

.preg_pfifo_lwm ( preg_pfifo_lwm ),
.preg_pfifo_hwm ( preg_pfifo_hwm ),
.preg_dma_bl ( preg_dma_bl ),

.rd_ck ( pck ),
.rd_reset ( reset_pfifo_pck ),
.rd ( pif_rd ),
.rd_early ( pif_rd_early ),
.rd_do ( pif_dat ),
.rd_dop ( pif_datp ),
.rd_okay ( pif_okay ),
.rd_cnt ( sreg_pi_rd_cnt ),
.rd_under ( sreg_pi_rd_under ),
.rd_over ( sreg_pi_rd_over )
);

// A little cruft to drive the DMA request out the correct
// pins.
//
wire [1:0] pi_dmareq;
reg pi_dmareq_d1;
always @(posedge pck)
pi_dmareq_d1 <= pif_okay;
assign pi_dmareq[0] = preg_dma_chan[0] ? pi_dmareq_d1 : 1'bz;
assign pi_dmareq[1] = preg_dma_chan[1] ? pi_dmareq_d1 : 1'bz;


// Internal perfipheral bus signals
//
reg [23:0] pib_addr;
wire [31:0] pib_di;
wire [31:0] pib_do;
wire [3:0] pib_dop;
reg [7:1] pib_csb;
reg pib_rd;
reg pib_oeb;
reg pib_web;
reg pib_lastb;

// Register all I/Os to power PC bus
//
wire pib_oectl;
pi_data pi_data_m (
.ck ( pck ),
.pad ( pi_data ),
.padp ( pi_par ),
.di ( pib_di ),
.do ( pib_do ),
.dop ( pib_dop ),
.oe ( pib_oectl )
);
always @(posedge pck) begin
pib_addr <= pi_addr;
pib_csb <= pi_csb;
pib_rd <= pi_rd;
pib_oeb <= pi_oeb;
pib_web <= pi_web;
pib_lastb <= pi_lastb;
end
assign pi_ready = 1'b1;
assign pi_err = 1'b0;

// specific control signal selection based on master/slave pin
//
reg master_d1;
reg master_d2;
wire pib_cs_reg;
wire pib_cs_dma;
wire pib_dma_req;
wire pib_reg_rd;
wire pib_reg_wr;
always @(posedge pck) begin
master_d1 <= master;
master_d2 <= master_d1;
end
assign pib_cs_reg = master_d2 ? ~pib_csb[2] : ~pib_csb[4];
assign pib_cs_dma = master_d2 ? ~pib_csb[3] : ~pib_csb[5];
assign pib_dma_req = pib_cs_dma & pib_rd;
assign pib_reg_rd = pib_cs_reg & pib_rd;
assign pib_reg_wr = pib_cs_reg & ~pib_rd;

// State machine for PPC bus interface. Handles DMA reads
// and register reads and writes.
//
parameter
ppc_idle = 3'd0,
ppc_dma_data = 3'd1,
ppc_regrd = 3'd2,
ppc_regrd2 = 3'd3,
ppc_regwr = 3'd4,
ppc_regwr2 = 3'd5,
ppc_end = 3'd6,
ppc_end2 = 3'd7;

reg [2:0] dmast, dmast_n;
reg [3:0] dmacnt, dmacnt_n;
reg dma_vld, dma_vld_n;
reg reg_vld, reg_vld_n;
reg do_dma_rd, do_dma_rd_n;
reg do_reg_wr, do_reg_wr_n;
reg do_ppc_oe; // not a flop
reg [1:0] do_dma_rd_early; // not a flop
reg do_ad_latch; // not a flop

always @(dmast or preset or pib_dma_req or dmacnt or preg_dma_bl or
pib_lastb or pib_reg_rd or pib_reg_wr) begin
do_ppc_oe = 1'b0;
do_ad_latch = 1'b0;
do_dma_rd_early = 2'b0;
do_dma_rd_n = 1'b0;
do_reg_wr_n = 1'b0;
dmacnt_n = dmacnt;
dma_vld_n = 1'b0;
reg_vld_n = 1'b0;
if (preset) begin
dmast_n = ppc_idle;
dmacnt_n = 4'd0;
end else begin
dmast_n = dmast;
case (dmast)
ppc_idle: begin
if (pib_dma_req) begin
dma_vld_n = 1'b1;
dmast_n = ppc_dma_data;
do_dma_rd_n = 1'b1;
do_ppc_oe = 1'b1;
if (pib_lastb) begin
// fixed length burst
do_dma_rd_early = 2'b01;
dmacnt_n = 4'd1;
end else begin
// scalar transfer
do_dma_rd_early = 2'b10;
dmacnt_n = preg_dma_bl;
end
end
if (pib_reg_rd) begin
do_ad_latch = 1'b1;
dmast_n = ppc_regrd;
end
if (pib_reg_wr) begin
do_ad_latch = 1'b1;
do_reg_wr_n = 1'b1;
dmast_n = ppc_regwr;
end
end

ppc_dma_data: begin
do_ppc_oe = 1'b1;
dma_vld_n = 1'b1;
dmacnt_n = dmacnt + 4'd1;
if (dmacnt == preg_dma_bl)
dmast_n = ppc_end;
else
do_dma_rd_n = 1'b1;
end

ppc_regrd: begin
reg_vld_n = 1'b1;
do_ppc_oe = 1'b1;
dmast_n = ppc_regrd2;
end

ppc_regrd2: begin
do_ppc_oe = 1'b1;
dmast_n = ppc_end;
end

ppc_regwr: begin
dmast_n = ppc_regwr2;
end

ppc_regwr2: begin
dmast_n = ppc_end;
end

ppc_end: begin
dmast_n = ppc_end2;
end

ppc_end2: begin
dmast_n = ppc_idle;
end
endcase
end
end
always @(posedge pck) begin
dmast <= dmast_n;
dmacnt <= dmacnt_n;
do_dma_rd <= do_dma_rd_n;
do_reg_wr <= do_reg_wr_n & (pib_addr[23:17] == 7'h01);
dma_vld <= dma_vld_n;
reg_vld <= reg_vld_n;
end

// Latch register address and write value
//
reg [15:0] reg_addr;
reg [15:0] reg_addr_sp;
reg [15:0] reg_wr_data;
reg [15:0] reg_rd_data;
always @(posedge pck) begin
if (do_ad_latch) begin
reg_addr <= pib_addr[16:1];
reg_wr_data <= pib_di[31:16];
end
if (do_ad_latch & (pib_addr[23:17] == 7'h00))
reg_addr_sp <= pib_addr[16:1];
else if (do_reg_wr & (reg_addr == `PREG_RD_ADDR))
reg_addr_sp <= reg_wr_data;
end

// Data out to PPC bus, oe, data, and parity
//
assign pib_oectl = ~do_ppc_oe;
assign pib_do = dma_vld ? pif_dat :
reg_vld ? {reg_rd_data, 16'b0} :
32'hffffffff;
assign pib_dop = dma_vld ? pif_datp : 4'hf;

// Reads from pi_fifo for DMA
//
assign pif_rd = do_dma_rd;
assign pif_rd_early = do_dma_rd_early;

PULLUP br0 ( board_rev[0] );
PULLUP br1 ( board_rev[1] );
PULLUP br2 ( board_rev[2] );
PULLUP br3 ( board_rev[3] );

PULLUP ar0 ( adc_rev[0] );
PULLUP ar1 ( adc_rev[1] );
PULLUP ar2 ( adc_rev[2] );
PULLUP ar3 ( adc_rev[3] );
PULLUP ar4 ( adc_rev[4] );
PULLUP ar5 ( adc_rev[5] );
PULLUP ar6 ( adc_rev[6] );
PULLUP ar7 ( adc_rev[7] );

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// Move fifo status registers to pck
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
reg [15:0] sreg_pstat1;
reg [15:0] sreg_pstat2;
reg [15:0] sreg_pstat3;
reg [15:0] sreg_pstat4;
reg [`GP_IN-1:0] sreg_gpin;
reg [`GP_IN-1:0] sreg_gpin_d1;
reg [`GP_IN-1:0] sreg_gpsticky;
reg reg_wr_sticky;
reg actually_running;
reg actually_running_pck;
wire [`GP_IN-1:0] gp_edge;

assign gp_edge = sreg_gpin & ~sreg_gpin_d1;
always @(posedge pck) begin
sreg_pstat1 <= {
5'b0,
sreg_pfifo_over, sreg_pfifo_under,
sreg_pi_wr_under, sreg_pi_wr_over,
sreg_pi_rd_cnt,
sreg_pi_rd_under, sreg_pi_rd_over };
sreg_pstat2 <= sp_id;
sreg_pstat3 <= {
4'b0,
board_rev,
adc_rev };
sreg_pstat4 <= sreg_pmax;
sreg_gpin <= gp;
sreg_gpin_d1 <= sreg_gpin;

actually_running_pck <= actually_running;
reg_wr_sticky <= do_reg_wr & (reg_addr == `PREG_GPSTICKY);
if (preset)
sreg_gpsticky <= `GP_IN'b0;
else if (reg_wr_sticky)
sreg_gpsticky <= ((gp_edge | sreg_gpsticky) & ~reg_wr_data);
else
sreg_gpsticky <= (gp_edge | sreg_gpsticky);
end

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// Move regsiter read data from ck domain to pck domain
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
reg [15:0] ctl_reg_rd_data;
always @(posedge pck)
ctl_reg_rd_data <= ctl_rd_data;

// write to pck registers
always @(posedge pck)
if (preset) begin
preg_test <= 16'd0;
preg_test2 <= 16'd0;
preg_pfifo_lwm <= 5'b0;
preg_pfifo_hwm <= 5'b0;
preg_dma_bl <= 4'b0;
preg_dma_chan <= 2'b0;
preg_pfifo_len_pck <= 20'b0;
preg_ramcfg_pck <= 8'b0;
preg_packdiag_pck <= 16'b0;
preg_reset <= 1'b0;
preg_reset_pfifo <= 1'b0;
preg_swap_pck <= 3'b000;
preg_clkctl <= 15'b0;
preg_gpoe <= `GP_IN 'b0;
preg_pidiag_pck <= 8'b0;
preg_gpsel_pck <= `GP_IN'b0;
preg_gpout_pck <= `GP_IN'b0;
preg_gpinv_pck <= `GP_IN'b0;
preg_gpintsel_pck <= `GP_IN'b0;
preg_gpforce_pck <= `GP_IN'b0;
preg_ppssel_pck <= 4'd0;
preg_run_pck <= 1'b0;
preg_adc0_off_pck <= `N_ADC'b0;
preg_adc1_off_pck <= `N_ADC'b0;
preg_adc2_off_pck <= `N_ADC'b0;
preg_adc3_off_pck <= `N_ADC'b0;
preg_adc0_scale_pck <= 16'h8000; // 1.0 in 1.15 unsigned
preg_adc1_scale_pck <= 16'h8000;
preg_adc2_scale_pck <= 16'h8000;
preg_adc3_scale_pck <= 16'h8000;
end else if (do_reg_wr) case (reg_addr)
`PREG_TEST: preg_test <= reg_wr_data;
`PREG_TEST2: preg_test2 <= reg_wr_data;
`PREG_PFIFO_LWM: preg_pfifo_lwm <= reg_wr_data[4:0];
`PREG_PFIFO_HWM: preg_pfifo_hwm <= reg_wr_data[4:0];
`PREG_DMA_BL: preg_dma_bl <= reg_wr_data[3:0];
`PREG_DMA_CHAN: preg_dma_chan <= reg_wr_data[1:0];
`PREG_PFIFO_LEN_H: preg_pfifo_len_pck[19:16] <= reg_wr_data[3:0];
`PREG_PFIFO_LEN_L: preg_pfifo_len_pck[15:0] <= reg_wr_data[15:0];
`PREG_RESET: preg_reset <= reg_wr_data[0];
`PREG_RESET_PFIFO: preg_reset_pfifo <= reg_wr_data[0];
`PREG_RAMCFG: preg_ramcfg_pck <= reg_wr_data[7:0];
`PREG_PACKDIAG: preg_packdiag_pck <= reg_wr_data;
`PREG_SWAP: preg_swap_pck <= reg_wr_data[2:0];
`PREG_CLKCTL: preg_clkctl <= reg_wr_data;
`PREG_GPOE: preg_gpoe <= reg_wr_data[`GP_IN-1:0];
`PREG_PIDIAG: preg_pidiag_pck <= reg_wr_data[7:0];
`PREG_GPSEL: preg_gpsel_pck <= reg_wr_data[`GP_IN-1:0];
`PREG_GPOUT: preg_gpout_pck <= reg_wr_data[`GP_IN-1:0];
`PREG_GPINV: preg_gpinv_pck <= reg_wr_data[`GP_IN-1:0];
`PREG_GPINTSEL: preg_gpintsel_pck <= reg_wr_data[`GP_IN-1:0];
`PREG_GPFORCE: preg_gpforce_pck <= reg_wr_data[`GP_IN-1:0];
`PREG_PPSSEL: preg_ppssel_pck <= reg_wr_data[3:0];
`PREG_RUN: preg_run_pck <= reg_wr_data[0];
`PREG_ADC0_OFFSET: preg_adc0_off_pck <= reg_wr_data[`N_ADC-1:0];
`PREG_ADC1_OFFSET: preg_adc1_off_pck <= reg_wr_data[`N_ADC-1:0];
`PREG_ADC2_OFFSET: preg_adc2_off_pck <= reg_wr_data[`N_ADC-1:0];
`PREG_ADC3_OFFSET: preg_adc3_off_pck <= reg_wr_data[`N_ADC-1:0];
`PREG_ADC0_SCALE: preg_adc0_scale_pck <= reg_wr_data;
`PREG_ADC1_SCALE: preg_adc1_scale_pck <= reg_wr_data;
`PREG_ADC2_SCALE: preg_adc2_scale_pck <= reg_wr_data;
`PREG_ADC3_SCALE: preg_adc3_scale_pck <= reg_wr_data;
endcase

// read pck registers
always @(posedge pck)
case (reg_addr)
`PREG_TEST: reg_rd_data <= preg_test;
`PREG_TEST2: reg_rd_data <= preg_test2;
`PREG_PSTAT1: reg_rd_data <= sreg_pstat1;
`PREG_PSTAT2: reg_rd_data <= sreg_pstat2;
`PREG_PSTAT3: reg_rd_data <= sreg_pstat3;
`PREG_PFIFO_LWM: reg_rd_data <= {11'b0, preg_pfifo_lwm};
`PREG_PFIFO_HWM: reg_rd_data <= {11'b0, preg_pfifo_hwm};
`PREG_DMA_BL: reg_rd_data <= {12'b0, preg_dma_bl};
`PREG_DMA_CHAN: reg_rd_data <= {14'b0, preg_dma_chan};
`PREG_PFIFO_LEN_H: reg_rd_data <= {12'b0, preg_pfifo_len_pck[19:16]};
`PREG_PFIFO_LEN_L: reg_rd_data <= preg_pfifo_len_pck[15:0];
`PREG_RD_DATA: reg_rd_data <= ctl_reg_rd_data;
`PREG_RESET: reg_rd_data <= {15'b0, preg_reset};
`PREG_RESET_PFIFO: reg_rd_data <= {15'b0, preg_reset_pfifo};
`PREG_RAMCFG: reg_rd_data <= {8'b0, preg_ramcfg_pck[7:0]};
`PREG_PACKDIAG: reg_rd_data <= preg_packdiag_pck;
`PREG_SWAP: reg_rd_data <= { 13'b0, preg_swap_pck };
`PREG_CLKCTL: reg_rd_data <= preg_clkctl;
`PREG_GPOE: reg_rd_data <= { 16-`GP_IN'b0, preg_gpoe };
`PREG_ADCCNT: reg_rd_data <= ck_wcnt_lat;
`PREG_PIDIAG: reg_rd_data <= {7'b0, preg_pidiag_pck};
`PREG_GPIN: reg_rd_data <= {16-`GP_IN'b0, sreg_gpin };
`PREG_GPSEL: reg_rd_data <= {16-`GP_IN'b0, preg_gpsel_pck};
`PREG_GPOUT: reg_rd_data <= {16-`GP_IN'b0, preg_gpout_pck};
`PREG_PMAX: reg_rd_data <= sreg_pstat4;
`PREG_GPINV: reg_rd_data <= {16-`GP_IN'b0, preg_gpinv_pck};
`PREG_GPINTSEL: reg_rd_data <= {16-`GP_IN'b0, preg_gpintsel_pck};
`PREG_GPFORCE: reg_rd_data <= {16-`GP_IN'b0, preg_gpforce_pck};
`PREG_PPSSEL: reg_rd_data <= {12'b0, preg_ppssel_pck};
`PREG_RUN: reg_rd_data <= {14'b0, actually_running_pck,
preg_run_pck};
`PREG_GPSTICKY: reg_rd_data <= {16-`GP_IN'b0, sreg_gpsticky};
`PREG_ADC0_OFFSET: reg_rd_data <= {16-`N_ADC'b0, preg_adc0_off_pck };
`PREG_ADC1_OFFSET: reg_rd_data <= {16-`N_ADC'b0, preg_adc1_off_pck };
`PREG_ADC2_OFFSET: reg_rd_data <= {16-`N_ADC'b0, preg_adc2_off_pck };
`PREG_ADC3_OFFSET: reg_rd_data <= {16-`N_ADC'b0, preg_adc3_off_pck };
`PREG_ADC0_SCALE: reg_rd_data <= preg_adc0_scale_pck;
`PREG_ADC1_SCALE: reg_rd_data <= preg_adc1_scale_pck;
`PREG_ADC2_SCALE: reg_rd_data <= preg_adc2_scale_pck;
`PREG_ADC3_SCALE: reg_rd_data <= preg_adc3_scale_pck;
`PREG_PLINTH_VER: reg_rd_data <= `PLINTH_VER;
default: reg_rd_data <= 16'b0;
endcase

reg ps_en_p1;
reg ps_en;
always @(posedge pck) begin
ps_en_p1 <= do_reg_wr & ~preset & (reg_addr == `PREG_CLKCTL);
ps_en <= ps_en_p1 & preg_clkctl[7];
end

// Clock control pins going off chip
//
assign adcclk_sel = preg_clkctl[0];
assign adcclk_en = preg_clkctl[1];
assign qdr_k_en = preg_clkctl[2]; // Enable K clock on QDR
assign qdr_c_en = preg_clkctl[3]; // Enable C clock on QDR
assign adc_dcm_reset = preg_clkctl[4];
assign adc_dcmret_reset = preg_clkctl[5];
assign ps_incdec = preg_clkctl[6];
assign ps_clk = pck;

assign gp_oeb = ~preg_gpoe;

// Register that leave this module need to be reclocked to
// ADC clock.
always @(posedge ck) begin
preg_pfifo_len <= preg_pfifo_len_pck;
preg_ramcfg <= preg_ramcfg_pck;
preg_packdiag <= preg_packdiag_pck;
preg_swap <= preg_swap_pck;
preg_pidiag <= preg_pidiag_pck;
preg_gpinv <= preg_gpinv_pck;
preg_gpsel <= preg_gpsel_pck;
preg_gpout <= preg_gpout_pck;
preg_gpintsel <= preg_gpintsel_pck;
preg_gpforce <= preg_gpforce_pck;
preg_ppssel <= preg_ppssel_pck;
preg_run <= preg_run_pck;
preg_adc0_off <= preg_adc0_off_pck;
preg_adc1_off <= preg_adc1_off_pck;
preg_adc2_off <= preg_adc2_off_pck;
preg_adc3_off <= preg_adc3_off_pck;
preg_adc0_scale <= preg_adc0_scale_pck;
preg_adc1_scale <= preg_adc1_scale_pck;
preg_adc2_scale <= preg_adc2_scale_pck;
preg_adc3_scale <= preg_adc3_scale_pck;
end

// Register write interface to ADC clock domain
// When the pi state machine emits a do_reg_wr_n (next cycle is
// a write in register space and bit-17 of the address is low,
// treat this is as a write to 128kbyte address space for
// config registers in the ADC clock domain (ck).
//
// This is a slightly parnoid way to reclock the register write
// to the ck domain. First we set a signal ctl_wren in the pck
// domain to indicate that we want to do a write. This is reclocked
// in the ck domain and delayed a couple of clock. This is then
// differenciated to procduce a write enable in the ck clock domain.
// The ck-domain-wren is reclock back into pck domain and used
// to clear the original ctl_wren in pck domain. This should be
// independent of the clock speed on either side of the interface.
// The only clock speed dependance might be if the ck clock is
// running really slow and software does writes too quickly.
//
reg ctl_wren;
reg ctl_wren_clr;
always @(posedge pck) begin
if (preset)
ctl_wren <= 1'b0;
else if (do_reg_wr_n & (pib_addr[23:17] == 7'h00))
ctl_wren <= 1'b1;
else if (ctl_wren_clr)
ctl_wren <= 1'b0;
end

// ck domain write interface to config registers
//
reg ctl_we;
reg [15:0] ctl_addr;
reg [15:0] ctl_data;

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// Reclock ctl_wren to the ck clock domain
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//
reg ctl_wren_p1_ck;
reg ctl_wren_ck;
reg ctl_wren_d1_ck;
reg ctl_wren_d2_ck;
always @(posedge ck) begin
ctl_wren_p1_ck <= ctl_wren;
ctl_wren_ck <= ctl_wren_p1_ck;
ctl_wren_d1_ck <= ctl_wren_ck;
ctl_wren_d2_ck <= ctl_wren_d1_ck;
ctl_we <= ctl_wren_ck & ~ctl_wren_d1_ck;
end

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// Take the delayed write enable back to pck domain.
// request.
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//
always @(posedge pck)
ctl_wren_clr <= ctl_wren_d2_ck;

// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
// Take address and write data to ck domain. This has
// at least 3-clocks to settle before ctl_we is asserted
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
//
always @(posedge ck) begin
ctl_addr <= reg_addr_sp;
ctl_data <= reg_wr_data;
end

// Temp to keep these unused pins
//
reg keep;
always @(posedge pck)
keep <= pib_oeb | pib_web | pib_addr[0] | ^pib_csb;

// Generate interrupts to power pc from fifo status.
// int0 is the interupt for a pack_fifo overflow, the
// usual cause is that the host isn't collecting data
// fast enough.
//
// int1 is a general failure, probably because of
// a bad register setting or a hardware bug.
// The master chip drives pi_int[1:0], the slave chip
// drive pi_int[3:2];
reg int0;
reg int1;
always @(posedge pck) begin
int0 <= sreg_pfifo_over;
int1 <= sreg_pfifo_under | sreg_pi_wr_under |
sreg_pi_wr_over | sreg_pi_rd_under |
sreg_pi_rd_over;
end
assign pi_int[0] = master ? int0 : 1'bz;
assign pi_int[1] = master ? int1 : 1'bz;
assign pi_int[2] = master ? 1'bz : int0;
assign pi_int[3] = master ? 1'bz : int1;

// Select PPS signal from one of the GP inputs using
// preg_ppssel. Use preg_run to generate a single pulse
// indicating the start of an observation and a single pulse
// at the end of observation when preg_run is written to 0.
//
reg pps_sel;
reg pps_int;
reg pps_d1, pps_d2;
always @(posedge ck) begin
pps_sel <= gp[preg_ppssel];
pps_d1 <= pps_sel;
pps_d2 <= pps_d1;
pps_int <= ~pps_d2 & pps_d1;
end

// Generate single pulse obs_start and obs_stop. obs_start
// is a single pulse on the first PPS after preg_run is written
// to one. obs_stop happens willy-nilly when preg_run is written
// back to zero.
//
// If preg_run is written to one and then written back to zero
// before a PPS arrives, no obs_start is sent to the signal
// processor, but a spurious obs_stop is sent to the sp.
//
reg obs_start_p1;
reg obs_start;
reg obs_stop;
reg preg_run_d1;
reg run_pending;
wire run_start = preg_run & ~preg_run_d1;
wire run_stop = ~preg_run & preg_run_d1;
always @(posedge ck) begin
if (reset | ~preg_run)
run_pending <= 1'b0;
else if (run_start)
run_pending <= 1'b1;
else if (obs_start_p1)
run_pending <= 1'b0;
preg_run_d1 <= preg_run;
obs_start_p1 <= run_pending & pps_int;
obs_start <= obs_start_p1;
obs_stop <= run_stop;

end

// Add reading for PPS... and "actually running"
//
always @(posedge ck) begin
if (reset | obs_stop)
actually_running <= 1'b0;
else if (obs_start)
actually_running <= 1'b1;
end
endmodule