summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--swpll.v127
1 files changed, 80 insertions, 47 deletions
diff --git a/swpll.v b/swpll.v
index 1a4bbf2..02e238b 100644
--- a/swpll.v
+++ b/swpll.v
@@ -17,7 +17,6 @@
// clock.
//
-
// The nominal input value to the NCO corresponds to a filter output
// of 2^20/5 ~ 0x33333. The loop filter output does not include the
// top fraction bit, so only 18 bits here, of which 4 bits
@@ -25,6 +24,30 @@
`define NCO_NOMINAL 18'h33333
//
+// The NCO counter is easily the most demanding data path in this
+// module. It relies of the filter module to stage the output in
+// suitable chunks to consume in each cycle (lest we end up with
+// mixed bits), so specify the number of bits per NCO
+// chunk here.
+//
+`define NCO_STAGE_BITS 3
+
+function integer imin(integer a, integer b);
+ imin = a < b ? a : b;
+endfunction // imin
+
+function integer imax(integer a, integer b);
+ imax = a >= b ? a : b;
+endfunction // imax
+
+function integer stages(integer total, integer perstage);
+ stages = (total+perstage-1)/perstage;
+endfunction // stages
+
+// This defines a range associated with a stage; the syntax is [`R(iter,bits,max,min)]
+`define R(i,b,ma,mi) (imin((((i)+1)*(b)+(mi)-1),(ma))):((i)*(b)+(mi))
+
+//
// Loop low pass filter. Run once per hsync strobe. The clk_en should
// be the appropriately shifted version of the hsync strobe so that
// the input value is suitably stable.
@@ -36,7 +59,6 @@
// Currently this is simply a first-order filter using a decaying
// average mechanism.
//
-(* ramstyle = "logic" *)
module swpll_loop_filter (
input rst_n,
input clk, // 360 MHz master clock
@@ -49,8 +71,11 @@ module swpll_loop_filter (
reg signed [17:0] acc;
wire signed [17:0] p1_ext = { p1, 3'b0 };
+ // The number of clock cycles to spread the setting of p2 out over
+ parameter p2_stages = stages(18,`NCO_STAGE_BITS);
+
(* ramstyle = "logic" *)
- reg [5:0] clk_en_q; // Delayed clock enables
+ reg [p2_stages:0] clk_en_q; // Delayed clock enables
// The higher this parameter is, the more narrow the bandwidth
parameter p1_shift = 4;
@@ -72,43 +97,45 @@ module swpll_loop_filter (
if (~rst_n)
begin
acc <= 18'b0;
- p2 <= `NCO_NOMINAL;
- clk_en_q <= 6'b0;
+ clk_en_q <= {(p2_stages+1){1'b0}};
end
else
begin
- clk_en_q <= { clk_en_q[4:0], clk_en };
-
- // Yes, this really is latched a whole input cycle
- // later. However, as the NCO counter is split 5
- // ways (see below), latch them in four stages to
- // match. We do this here rather than further down,
- // because doing it here is almost free.
-
- if (clk_en_q[0])
- p2[3:0] <= next_p2[3:0];
- if (clk_en_q[1])
- p2[7:4] <= next_p2[7:4];
- if (clk_en_q[2])
- p2[11:8] <= next_p2[11:8];
- if (clk_en_q[3])
- p2[15:12] <= next_p2[15:12];
- if (clk_en_q[4])
- p2[17:16] <= next_p2[17:16];
+ clk_en_q <= (clk_en_q << 1) | clk_en;
// Only do this after the full value has been latched from
// the previous computation; this also allows p1 to
// stabilize (it is a multicycle path.)
- if (clk_en_q[5])
+ if (clk_en_q[p2_stages])
acc <= acc + ((p1_ext - acc) >>> p1_shift);
end
+
+ // Yes, this really is latched a whole input cycle
+ // later. However, as the NCO counter is split into stages
+ // ways (see below), latch them in stages here too, so that
+ // the NCO seems them consistently. Do it here rather than in
+ // the NCO module, because it is more efficient.
+ wire [17:0] p2_init = `NCO_NOMINAL;
+
+ generate
+ genvar i;
+ for (i = 0; i < p2_stages; i = i + 1)
+ begin : p2out
+ always @(posedge clk or negedge rst_n)
+ if (~rst_n)
+ p2[`R(i,`NCO_STAGE_BITS,17,0)]
+ <= p2_init[`R(i,`NCO_STAGE_BITS,17,0)];
+ else
+ p2[`R(i,`NCO_STAGE_BITS,17,0)]
+ <= next_p2[`R(i,`NCO_STAGE_BITS,17,0)];
+ end
+ endgenerate
endmodule // swpll_loop_filter
// Extract a hsync strobe from the composite sync signal without
// relying on any generated clocks. hsync is identified by a level
// change in the sync output no sooner than ~10-11 ┬Ás (3840-4096 clk
// cycles) after the previous one.
-(* ramstyle = "logic" *)
module detect_hsync (
input rst_n,
input clk,
@@ -154,7 +181,6 @@ module detect_hsync (
end // else: !if(~rst_n)
endmodule // detect_hsync
-(* ramstyle = "logic" *)
module swpll (
input rst_n,
input clk, // 360 MHz master clock
@@ -260,7 +286,8 @@ module swpll (
// filter output.
//
reg [-1:-20] nco_ctr; // The actual NCO counter
- reg [4:0] nco_ctr_cy; // Staged carries
+ parameter nco_stages = stages(20,`NCO_STAGE_BITS);
+ reg [nco_stages:0] nco_ctr_cy; // Staged carries; carry 0 is always 0
//
// This takes the accumulated phase error and turns it into
@@ -275,28 +302,34 @@ module swpll (
// which lets us do [5:4:4:4:4] for {clk_stb_q, nco_ctr}; this appears to
// give the best timing balance.
//
+
wire [-1:-20] nco_input = { 2'b0, p2 };
reg clk_stb_q;
- always @(posedge clk or negedge rst_n)
- if (~rst_n)
- begin
- nco_ctr <= 20'b0;
- nco_ctr_cy <= 5'b0;
- end
- else
- begin
- { nco_ctr_cy[0], nco_ctr[-17:-20] }
- <= nco_ctr[-17:-20] + nco_input[-17:-20];
- { nco_ctr_cy[1], nco_ctr[-13:-16] }
- <= nco_ctr[-13:-16] + nco_input[-13:-16] + nco_ctr_cy[0];
- { nco_ctr_cy[2], nco_ctr[-9:-12] }
- <= nco_ctr[-9:-12] + nco_input[-9:-12] + nco_ctr_cy[1];
- { nco_ctr_cy[3], nco_ctr[-5:-8] }
- <= nco_ctr[-5:-8] + nco_input[-5:-8] + nco_ctr_cy[2];
- { nco_ctr_cy[4], nco_ctr[-1:-4] }
- <= nco_ctr[-1:-4] + nco_input[-1:-4] + nco_ctr_cy[3];
- end
+ // Permanent condition...
+ always @(*)
+ nco_ctr_cy[0] = 1'b0;
+
+ generate
+ genvar i;
+ for (i = 0; i < nco_stages; i = i + 1)
+ begin : nco
+ always @(posedge clk or negedge rst_n)
+ if (~rst_n)
+ begin
+ nco_ctr_cy[i+1] <= 1'b0;
+ nco_ctr[`R(i,`NCO_STAGE_BITS,-1,-20)]
+ <= {`NCO_STAGE_BITS{1'b0}};
+ end
+ else
+ begin
+ { nco_ctr_cy[i+1], nco_ctr[`R(i,`NCO_STAGE_BITS,-1,-20)] }
+ <= nco_ctr[`R(i,`NCO_STAGE_BITS,-1,-20)] +
+ nco_input[`R(i,`NCO_STAGE_BITS,-1,-20)] +
+ nco_ctr_cy[i];
+ end // else: !if(~rst_n)
+ end // block: nco
+ endgenerate
reg out_clk_q; // Output clock buffer register
reg out_clk_stb_q;
@@ -313,7 +346,7 @@ module swpll (
else
begin
out_clk_q <= ~nco_ctr[-1];
- out_clk_stb_q <= nco_ctr_cy[4];
+ out_clk_stb_q <= nco_ctr_cy[nco_stages];
end
assign clk_stb = out_clk_stb_q;