// ============================================================================ // f193.v — 54F/74F193 Up/Down Binary Counter with Separate Up/Down Clocks // // Fairchild FAST (Advanced Schottky TTL) // Source: docs/devices/54F74F193.txt (1980 Fairchild FAST Data Book, // pages 4-61 ... 4-64, 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. MR LOW, /PL LOW : asynchronous parallel load of Pn, // overriding clocks // 3. MR LOW, /PL HIGH, CPU ^, CPD H : count up on rising CPU edge // 4. MR LOW, /PL HIGH, CPU H, CPD ^ : count down on rising CPD edge // 5. MR LOW, /PL HIGH, CPU H, CPD H : no change // // Count sequence is modulo-16 binary. UP: 0->1->...->15->0 // DOWN: 0->15->...->1->0 // // Warning: While counting with one clock input, the other SHOULD BE HELD HIGH. // // Terminal count equations (data sheet p. 4-62): // /TCU = Q0·Q1·Q2·Q3·/CPU [count=15 and CPU LOW] // /TCD = /Q0·/Q1·/Q2·/Q3·/CPD [count=0 and CPD LOW] // // Pin-for-pin identical to the 'F192 (same pinout, same mode select table). // // 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). The table lists TYP values only // (Min columns blank, datasheet marked PRELIMINARY), so each specparam carries // the typ value alone. // // 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 f193 ( input wire mr, // Pin 14 — MR Master Reset (active HIGH) input wire pl_n, // Pin 11 — /PL Parallel Load input (active LOW) input wire cpu, // Pin 5 — CPU Count Up Clock input wire cpd, // Pin 4 — CPD Count Down Clock input wire p0, // Pin 15 — P0 Parallel data input 0 input wire p1, // Pin 1 — P1 Parallel data input 1 input wire p2, // Pin 10 — P2 Parallel data input 2 input wire p3, // Pin 9 — P3 Parallel data input 3 output wire q0, // Pin 3 — Q0 Flip-flop output 0 output wire q1, // Pin 2 — Q1 Flip-flop output 1 output wire q2, // Pin 6 — Q2 Flip-flop output 2 output wire q3, // Pin 7 — Q3 Flip-flop output 3 output wire tcu_n, // Pin 12 — /TCU Terminal Count Up / Carry (active LOW) output wire tcd_n // Pin 13 — /TCD Terminal Count Down / Borrow (active LOW) ); // 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: a clock left LOW during and after // a reset or load then counts on its next rise, as the data sheet // requires, while an edge arriving *during* a reset or load is consumed // by the branch that overrides it and leaves nothing pending. 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 <= state + 4'd1; else if (cpd && !cpd_d) state <= state - 4'd1; 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 {q3, q2, q1, q0} = cnt; assign tcu_n = ~(&cnt & ~cpu); assign tcd_n = ~(~|cnt & ~cpd); specify // Propagation delay CPU or CPD to Qn (tPLH 4.5, tPHL 5.5 ns typ) specparam tlh_cp_q = 4.5; specparam thl_cp_q = 5.5; // Propagation delay CPU to /TCU (tPLH 5.0, tPHL 4.5 ns typ) specparam tlh_cpu_tcu = 5.0; specparam thl_cpu_tcu = 4.5; // Propagation delay CPD to /TCD (tPLH 5.0, tPHL 4.5 ns typ) specparam tlh_cpd_tcd = 5.0; specparam thl_cpd_tcd = 4.5; // Propagation delay /PL to Qn (tPLH 5.7, tPHL 6.2 ns typ) specparam tlh_pl_q = 5.7; specparam thl_pl_q = 6.2; // Propagation delay MR to Qn (tPHL 5.2 ns typ; MR can only drive Q LOW) specparam thl_mr_q = 5.2; // Propagation delay MR to /TCU (tPLH 7.5 ns typ; MR=H forces Q LOW, // releasing terminal count, so /TCU rises) specparam tlh_mr_tcu = 7.5; // Propagation delay MR to /TCD (tPHL 5.5 ns typ) specparam thl_mr_tcd = 5.5; // Propagation delay /PL to /TCU (tPLH 8.5 ns typ; /PL falling // loads cnt; resulting cnt+CPU may change /TCU) specparam tlh_pl_tcu = 8.5; // Propagation delay /PL to /TCD (tPHL 8.5 ns typ) specparam thl_pl_tcd = 8.5; // CPU/CPD to Q outputs (cpu => q0) = (tlh_cp_q, thl_cp_q); (cpu => q1) = (tlh_cp_q, thl_cp_q); (cpu => q2) = (tlh_cp_q, thl_cp_q); (cpu => q3) = (tlh_cp_q, thl_cp_q); (cpd => q0) = (tlh_cp_q, thl_cp_q); (cpd => q1) = (tlh_cp_q, thl_cp_q); (cpd => q2) = (tlh_cp_q, thl_cp_q); (cpd => q3) = (tlh_cp_q, thl_cp_q); // CPU to /TCU, CPD to /TCD (cpu => tcu_n) = (tlh_cpu_tcu, thl_cpu_tcu); (cpd => tcd_n) = (tlh_cpd_tcd, thl_cpd_tcd); // /PL to Qn (asynchronous parallel load gate) (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 to Qn (asynchronous reset — only tPHL applies) (mr => q0) = (thl_mr_q); (mr => q1) = (thl_mr_q); (mr => q2) = (thl_mr_q); (mr => q3) = (thl_mr_q); // MR to /TCU (tPLH only — MR=H forces Q LOW, releasing /TCU) (mr => tcu_n) = (tlh_mr_tcu); // MR to /TCD (tPHL only) (mr => tcd_n) = (thl_mr_tcd); // /PL to /TCU (tPLH only) (pl_n => tcu_n) = (tlh_pl_tcu); // /PL to /TCD (tPHL only) (pl_n => tcd_n) = (thl_pl_tcd); // AC operating requirements (data sheet, +25 C 5.0 V minima): // ts(H/L) Pn to /PL 5.0, th(H/L) Pn to /PL 3.0; // tw(L) /PL 5.0, tw(L) CPU 5.5, tw(L) CPD 5.5, tw(H) MR 5.5; // trec /PL to CPU or CPD 6.0, trec MR to CPU or CPD 6.0. // 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 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 = 6.0; $setup(p0, negedge pl_n, ts_p); $setup(p1, negedge pl_n, ts_p); $setup(p2, negedge pl_n, ts_p); $setup(p3, negedge pl_n, ts_p); $hold(negedge pl_n, p0, th_p); $hold(negedge pl_n, p1, th_p); $hold(negedge pl_n, p2, th_p); $hold(negedge 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); $recovery(posedge pl_n, posedge cpd, trec); $recovery(negedge mr, posedge cpu, trec); $recovery(negedge mr, posedge cpd, trec); `endif endspecify endmodule