// ============================================================================ // f191.v — 54F/74F191 Up/Down Binary Counter with Preset and Ripple Clock // // Fairchild FAST (Advanced Schottky TTL) // Source: docs/devices/54F74F191.txt (1980 Fairchild FAST Data Book, // pages 4-52 ... 4-56) // // Modes of operation, in order of precedence (data sheet Mode Select table): // 1. /PL LOW : asynchronous parallel load — Pn // appears on Qn immediately, // overriding all other inputs // 2. /PL HIGH, /CE LOW, /U/D LOW, CP ^ : count up // 3. /PL HIGH, /CE LOW, /U/D HIGH, CP ^: count down // 4. /PL HIGH, /CE HIGH : hold (no change) // // Count sequence is modulo-16 binary. UP: 0->1->...->15->0. DOWN: 0->15->...->1->0. // No illegal states. // // TC is normally LOW. Goes HIGH when count = 15 in count-up mode or // count = 0 in count-down mode. Remains HIGH until a state change (count // or preset) or a /U/D change. // // /RC is normally HIGH. When /CE = LOW and TC = HIGH, /RC follows CP: // /RC = CP (same polarity). Otherwise /RC = HIGH. // // Timing values from the data sheet AC Characteristics table, // 54F/74F column (T_A = +25 C, V_CC = +5.0 V, C_L = 15 pF), min:typ:max ns. // // Ports are scalar and named after the data sheet pin names: Icarus Verilog // does not fully support multi-bit (parallel) specify path connections, so // vector ports would get incorrect per-bit delays. // ============================================================================ `timescale 1ns/100ps module f191 ( input wire cp, // clock pulse (active rising edge) input wire pl_n, // parallel load (active LOW, asynchronous) input wire ce_n, // count enable (active LOW) input wire ud_n, // up/down count control (LOW = count up) input wire p0, // parallel data input 0 input wire p1, // parallel data input 1 input wire p2, // parallel data input 2 input wire p3, // parallel data input 3 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 tc, // terminal count (active HIGH) output wire rc_n // ripple clock (active LOW) ); // /PL idles HIGH, hence the previous-level register's initial value. A // CP edge arriving while /PL is LOW is consumed by the load branch, which // has priority, and leaves nothing pending; a CP edge after the load // counts normally, since only a genuine rising edge fires the block. reg [3:0] state; reg pl_d = 1'b1; always @(posedge cp or posedge pl_n or negedge pl_n) begin if (!pl_n || !pl_d) state <= {p3, p2, p1, p0}; else if (!ce_n) state <= ud_n ? state - 4'd1 : state + 4'd1; pl_d <= pl_n; end // /PL passes P straight to the outputs, overriding 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 = !pl_n ? {p3, p2, p1, p0} : state; assign q0 = cnt[0]; assign q1 = cnt[1]; assign q2 = cnt[2]; assign q3 = cnt[3]; wire tc_int; assign tc_int = (!ud_n && cnt == 4'd15) || (ud_n && cnt == 4'd0); assign tc = tc_int; assign rc_n = (!ce_n && tc_int) ? cp : 1'b1; specify // CP to Qn: tPLH 2.0:4.5:8.0, tPHL 2.0:5.5:9.0 specparam tlh_cp_q = 2.0:4.5:8.0; specparam thl_cp_q = 2.0:5.5:9.0; // CP to TC: tPLH 3.0:6.5:10, tPHL 4.0:8.5:12 specparam tlh_cp_tc = 3.0:6.5:10; specparam thl_cp_tc = 4.0:8.5:12; // CP to /RC: tPLH 2.0:4.5:7.0, tPHL 2.0:4.0:7.0 specparam tlh_cp_rc = 2.0:4.5:7.0; specparam thl_cp_rc = 2.0:4.0:7.0; // /CE to /RC: tPLH 2.0:3.6:6.0, tPHL 2.0:3.5:6.0 specparam tlh_ce_rc = 2.0:3.6:6.0; specparam thl_ce_rc = 2.0:3.5:6.0; // /U/D to /RC: tPLH 6.0:10:16, tPHL 4.0:8.0:12 specparam tlh_ud_rc = 6.0:10:16; specparam thl_ud_rc = 4.0:8.0:12; // /U/D to TC: tPLH 2.0:5.0:9.0, tPHL 2.0:5.5:9.0 specparam tlh_ud_tc = 2.0:5.0:9.0; specparam thl_ud_tc = 2.0:5.5:9.0; // Pn to Qn: tPLH 2.0:3.6:6.0, tPHL 3.0:6.3:10 specparam tlh_p_q = 2.0:3.6:6.0; specparam thl_p_q = 3.0:6.3:10; // /PL to Qn: tPLH 2.0:5.7:9.0, tPHL 3.0:6.2:10 specparam tlh_pl_q = 2.0:5.7:9.0; specparam thl_pl_q = 3.0:6.2:10; (cp => q0) = (tlh_cp_q, thl_cp_q); (cp => q1) = (tlh_cp_q, thl_cp_q); (cp => q2) = (tlh_cp_q, thl_cp_q); (cp => q3) = (tlh_cp_q, thl_cp_q); (cp => tc) = (tlh_cp_tc, thl_cp_tc); (cp => rc_n) = (tlh_cp_rc, thl_cp_rc); (ce_n => rc_n) = (tlh_ce_rc, thl_ce_rc); (ud_n => rc_n) = (tlh_ud_rc, thl_ud_rc); (ud_n => tc) = (tlh_ud_tc, thl_ud_tc); (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); // AC operating requirements (data sheet, +25 C 5.0 V minima) // Icarus Verilog does not support timing checks; kept (guarded) // for simulators that do. `ifndef __ICARUS__ specparam ts_p = 5.0; specparam th_p = 3.0; specparam ts_ce = 10.0; specparam th_ce = 0; specparam tw_pl_l = 5.0; specparam tw_cp_l = 5.5; specparam trec_pl = 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); $setup(ce_n, posedge cp, ts_ce); $hold(posedge cp, ce_n, th_ce); $width(negedge pl_n, tw_pl_l); $width(negedge cp, tw_cp_l); $recovery(posedge pl_n, posedge cp, trec_pl); `endif endspecify endmodule