// ============================================================================ // f192.v — 54F/74F192 Up/Down Decade Counter with Separate Up/Down Clocks // // Fairchild FAST (Advanced Schottky TTL) // Source: docs/devices/54F74F192.txt (1980 Fairchild FAST Data Book, // pages 4-57 ... 4-60, PRELIMINARY) // // Modes of operation, in order of precedence (data sheet Mode Select table): // 1. MR HIGH : asynchronous master reset — all Q // forced LOW immediately, overriding // all other inputs // 2. /PL LOW : asynchronous parallel load of Pn, // regardless of clock inputs // 3. MR LOW, /PL HIGH, CPU & CPD HIGH : no change // 4. MR LOW, /PL HIGH, CPU rising, CPD HIGH : count up // 5. MR LOW, /PL HIGH, CPD rising, CPU HIGH : count down // // WARNING: While counting with one clock input, the other should be held // HIGH. Otherwise the circuit will count by twos or not at all. // // Count sequence: BCD decade 0-9. Illegal states 10-15 recover as follows: // Counting UP: 10->11->6, 12->13->4, 14->15->2 // Counting DOWN: 15->14->13->12->11->10->9 (enters legal at 9) // // Terminal count equations (from datasheet): // /TCU = Q0 * Q3 * /CPU // /TCD = /Q0 * /Q1 * /Q2 * /Q3 * /CPD // // Timing from PRELIMINARY data sheet AC Characteristics, 54F/74F column // (T_A = +25 C, V_CC = +5.0 V, C_L = 15 pF) — typ values only // (Min/Max columns blank). // // Ports are scalar and named after the data sheet pin names: Icarus Verilog // does not fully support multi-bit specify path connections. // ============================================================================ `timescale 1ns/100ps module f192 ( input wire p1, // parallel data input 1 input wire p2, // parallel data input 2 input wire p3, // parallel data input 3 input wire cpd, // count down clock (active rising edge) input wire cpu, // count up clock (active rising edge) input wire pl_n, // parallel load (active LOW) input wire mr, // master reset (active HIGH) input wire p0, // parallel data input 0 output wire q0, // flip-flop output 0 output wire q1, // flip-flop output 1 output wire q2, // flip-flop output 2 output wire q3, // flip-flop output 3 output wire tcu_n, // terminal count up / carry (active LOW) output wire tcd_n // terminal count down / borrow (active LOW) ); // Count sequences, per the State Diagram. Counting up, the illegal // states recover along 10 -> 11 -> 6, 12 -> 13 -> 4 and 14 -> 15 -> 2; // counting down they simply chain 15 -> ... -> 10 -> 9 into the legal // sequence, which is a plain decrement. function [3:0] count_up; input [3:0] c; case (c) 4'd9: count_up = 4'd0; 4'd10: count_up = 4'd11; 4'd11: count_up = 4'd6; 4'd12: count_up = 4'd13; 4'd13: count_up = 4'd4; 4'd14: count_up = 4'd15; 4'd15: count_up = 4'd2; default: count_up = c + 4'd1; endcase endfunction function [3:0] count_dn; input [3:0] c; count_dn = (c == 4'd0) ? 4'd9 : c - 4'd1; endfunction // Both clocks idle HIGH, so a rising edge is "HIGH now, LOW at the // previous event" and the previous-level registers start HIGH. They // track their clocks unconditionally, which is what makes a clock left // LOW across a reset or load count on its next rise, as the data sheet // requires. reg [3:0] state; reg cpu_d = 1'b1; reg cpd_d = 1'b1; reg pl_d = 1'b1; always @(posedge cpu or negedge cpu or posedge cpd or negedge cpd or posedge mr or posedge pl_n or negedge pl_n) begin if (mr) state <= 4'd0; else if (!pl_n || !pl_d) state <= {p3, p2, p1, p0}; else if (cpu && !cpu_d) state <= count_up(state); else if (cpd && !cpd_d) state <= count_dn(state); cpu_d <= cpu; cpd_d <= cpd; pl_d <= pl_n; end // MR latches the outputs LOW and /PL passes P straight to them; both // override the state register, which is what makes the load transparent // to P while /PL is LOW. The `!pl_d` term above recaptures P into the // state register when /PL is released. wire [3:0] cnt = mr ? 4'd0 : !pl_n ? {p3, p2, p1, p0} : state; assign q0 = cnt[0]; assign q1 = cnt[1]; assign q2 = cnt[2]; assign q3 = cnt[3]; assign tcu_n = ~(cnt[0] & cnt[3] & ~cpu); assign tcd_n = ~(~cnt[0] & ~cnt[1] & ~cnt[2] & ~cnt[3] & ~cpd); specify // AC characteristics (PRELIMINARY data sheet, typ values only, // T_A = +25 C, V_CC = +5.0 V, C_L = 15 pF) // CPU or CPD to Qn: tPLH 4.5, tPHL 5.5 ns typ specparam tlh_clk_q = 4.5; specparam thl_clk_q = 5.5; // CPU to /TCU: tPLH 5.0, tPHL 4.5 ns typ specparam tlh_cpu_tcu = 5.0; specparam thl_cpu_tcu = 4.5; // CPD to /TCD: tPLH 5.0, tPHL 4.5 ns typ specparam tlh_cpd_tcd = 5.0; specparam thl_cpd_tcd = 4.5; // Pn to Qn: tPLH 3.6, tPHL 6.3 ns typ specparam tlh_p_q = 3.6; specparam thl_p_q = 6.3; // /PL to Qn: tPLH 5.7, tPHL 6.2 ns typ specparam tlh_pl_q = 5.7; specparam thl_pl_q = 6.2; // MR to Qn: tPHL 5.2 ns typ (MR can only drive Q LOW) specparam thl_mr_q = 5.2; // MR to /TCU: tPLH 7.5 ns typ specparam tlh_mr_tcu = 7.5; // MR to /TCD: tPHL 5.5 ns typ specparam thl_mr_tcd = 5.5; // /PL to /TCU: tPLH 8.5 ns typ specparam tlh_pl_tcu = 8.5; // /PL to /TCD: tPHL 8.5 ns typ specparam thl_pl_tcd = 8.5; // Pn to /TCU or /TCD: tPLH 8.5, tPHL 6.7 ns typ specparam tlh_p_tcx = 8.5; specparam thl_p_tcx = 6.7; (cpu => q0) = (tlh_clk_q, thl_clk_q); (cpu => q1) = (tlh_clk_q, thl_clk_q); (cpu => q2) = (tlh_clk_q, thl_clk_q); (cpu => q3) = (tlh_clk_q, thl_clk_q); (cpd => q0) = (tlh_clk_q, thl_clk_q); (cpd => q1) = (tlh_clk_q, thl_clk_q); (cpd => q2) = (tlh_clk_q, thl_clk_q); (cpd => q3) = (tlh_clk_q, thl_clk_q); (cpu => tcu_n) = (tlh_cpu_tcu, thl_cpu_tcu); (cpd => tcd_n) = (tlh_cpd_tcd, thl_cpd_tcd); (p0 => q0) = (tlh_p_q, thl_p_q); (p1 => q1) = (tlh_p_q, thl_p_q); (p2 => q2) = (tlh_p_q, thl_p_q); (p3 => q3) = (tlh_p_q, thl_p_q); (pl_n => q0) = (tlh_pl_q, thl_pl_q); (pl_n => q1) = (tlh_pl_q, thl_pl_q); (pl_n => q2) = (tlh_pl_q, thl_pl_q); (pl_n => q3) = (tlh_pl_q, thl_pl_q); (mr => q0) = (thl_mr_q); (mr => q1) = (thl_mr_q); (mr => q2) = (thl_mr_q); (mr => q3) = (thl_mr_q); (mr => tcu_n) = (tlh_mr_tcu); (mr => tcd_n) = (thl_mr_tcd); (pl_n => tcu_n) = (tlh_pl_tcu); (pl_n => tcd_n) = (thl_pl_tcd); (p0 => tcu_n) = (tlh_p_tcx, thl_p_tcx); (p1 => tcu_n) = (tlh_p_tcx, thl_p_tcx); (p2 => tcu_n) = (tlh_p_tcx, thl_p_tcx); (p3 => tcu_n) = (tlh_p_tcx, thl_p_tcx); (p0 => tcd_n) = (tlh_p_tcx, thl_p_tcx); (p1 => tcd_n) = (tlh_p_tcx, thl_p_tcx); (p2 => tcd_n) = (tlh_p_tcx, thl_p_tcx); (p3 => tcd_n) = (tlh_p_tcx, thl_p_tcx); // AC operating requirements (guarded for non-Icarus simulators) `ifndef __ICARUS__ specparam ts_p = 5.0; specparam th_p = 3.0; specparam tw_pl_l = 5.0; specparam tw_cpu_l = 5.5; specparam tw_cpd_l = 5.5; specparam tw_mr_h = 5.5; specparam trec_pl = 6.0; specparam trec_mr = 6.0; $setup(p0, posedge pl_n, ts_p); $setup(p1, posedge pl_n, ts_p); $setup(p2, posedge pl_n, ts_p); $setup(p3, posedge pl_n, ts_p); $hold(posedge pl_n, p0, th_p); $hold(posedge pl_n, p1, th_p); $hold(posedge pl_n, p2, th_p); $hold(posedge pl_n, p3, th_p); $width(negedge pl_n, tw_pl_l); $width(negedge cpu, tw_cpu_l); $width(negedge cpd, tw_cpd_l); $width(posedge mr, tw_mr_h); $recovery(posedge pl_n, posedge cpu, trec_pl); $recovery(posedge pl_n, posedge cpd, trec_pl); $recovery(negedge mr, posedge cpu, trec_mr); $recovery(negedge mr, posedge cpd, trec_mr); `endif endspecify endmodule