RFC 1936
Implementing the Internet Checksum in Hardware
RFC 1936: Implementing the Internet Checksum in Hardware π
Okay bestie, first the paperwork π. This is RFC 1936, titled Implementing the Internet Checksum in Hardware. It was written by J. Touch and B. Parham, both at ISI, and it's dated April 1996. The category is Informational, so it shares info with the Internet community but doesn't set a standard β¨. It doesn't obsolete or update any other RFCs.
Status of This Memo π
This memo provides information for the Internet community. It does not specify an Internet standard of any kind. Distribution of this memo is unlimited π.
Abstract β¨
This memo presents a technique for efficiently implementing the Internet Checksum in hardware π₯. It includes PLD code for programming a single, low cost part to perform checksumming at 1.26 Gbps π .
Introduction π¦
Okay so the Internet Checksum is used in a bunch of Internet protocols to catch data corruption. It's used in headers (e.g., IP) [4] and in packet bodies (e.g, UDP, TCP) [5][6]. People have already written about how to do this efficiently in software in earlier RFCs [1][2][3][7], so that's covered π«Ά.
Here's the twist: efficient software versions of the checksum are often squished into data copying operations ([1], Section 2). Copying data is increasingly done by dedicated direct memory access (DMA) hardware, so DMA designs are starting to include special hardware that computes the Internet Checksum during the transfer. It's giving hardware glow-up ππΈ.
This note lays out the architecture of an efficient, pipelined Internet Checksum mechanism that's a good fit for DMA hardware [8] β¨. It can run on a relatively inexpensive programmable logic device (PLD), which cost about $40 in 1995 πΈ, and it handles 1.26 Gbps transfer rates, at 26 ns per 32-bit word β. Appendix A has the pseudocode for this device π§. The design has already been built into the PC-ATOMIC host interface hardware [8] π. The authors believe this design is of general use to the Internet community, and honestly? Same π.
The rest of the doc is organized like this:
Review of the Internet Checksum
One's Complement vs. Two's Complement Addition
Interfaces
Summary
Appendix A - PLD source code
Main character energy, and it's all coming up next π¦π
Interfaces β¨
Okay so this device lives on a VL-Bus PC host interface card [8] π». It has both a hardware interface and a software interface, and here's how they work π«Ά.
Hardware Interface π
The Internet Checksum hardware shows up as a single-port 32-bit register with clock and control signals [8], and it looks like this:
+----------------------+
CLR--->| |
OE---->| 32-bit register as |
CLK--->| 2 adjacent 16-bit |<---/---> 32-bit data bus
ICLK-->| ones-complement sums |
ADD--->| |
+----------------------+
CLR = zero the register
OE = write the register onto the data bus
CLK = clock to cycle the pipeline operation
ICLK = input data latch clock
ADD = initiating an add of latched input data
CLR wipes the checksum register and the input latch back to zero π§Ή. There's no explicit load, bestie, but a CLR followed by a write of the load value to a dummy location does the same thing β¨.
OE either writes the register onto the data bus or tri-states it π.
CLK keeps the pipeline moving πββοΈ. If no new input data is latched to be added (via ICLK, ADD), a virtual "zero" gets summed into the register so the pipeline can empty out π«§.
ICLK transparently latches whatever is on the data bus, so it can be summed into the accumulator on the next ADD signal π§². The ADD signal then pushes that latched input data into the checksum pipeline π . ADD and ICLK are commonly tied together π. One 32-bit data value can be latched and accumulated into the pipeline adder every 26-ns clock, as long as the data is stable when the ADD/ICLK signal happens β±οΈ. It's giving speed π₯.
The internal 32-bit register is organized as two 16-bit ones-complement sums, one over the even 16-bit words of the data stream and one over the odd ones π. To get the Internet Checksum out of that, ones-complement add the two halves together, then invert the result π¦.
Software Interface πΎ
The device is used as a memory-mapped register. You read the register by reading its equivalent memory location π.
The device is controlled through an external memory-mapped register. Bits in this control register clear the device (set/clear the CLR line), and enable and disable the device (set/clear the ADD line) ποΈ. The CLR line can instead be mapped to a memory write. That way, reading the location is a non-destructive read of the checksum register, and writing any value clears the checksum register π§Ό. The enable/disable control must be stored in an external register.
The device is designed to run in the background during memory transfers, whether DMA or programmed I/O ποΈ. Once it's enabled, every transfer across that bus gets summed into the checksum register π. The checksum is available 5 clocks after the last enabled data accumulation. This delay is often hidden by memory access mechanisms and bus arbitration π. If it's needed, "stall" instructions can be executed for the appropriate delay β.
For the following example, we assume the device is located at CKSUMLOC, and that reading that location reads the checksum register while writing any value to it clears the register π§. The control register is located at CTLLOC, and the checksum enable/disable bit is CKSUMBIT, where 1 is enabled and 0 is disabled. To perform a checksum, a programmer clears the register, (optionally) initializes the checksum, starts a series of transfers, and then uses the result π:
/******* initialization *******/
*(CTLLOC) &= ~((ctlsize)(CKSUMBIT)); /* disable sum */
(word32)(*(CKSUMLOC)) = 0; /* clear reg */
*(CTLLOC) |= CKSUMBIT; /* enable sum */
{ (optional) write initial value to a dummy location }
/***** perform a transfer *****/
{ do one or more DMA or PIO transfers - read or write }
/***** gather the results *****/
*(CTLLOC) &= ~((ctlsize)(CKSUMBIT)); /* disable sum */
sum = (word32)(*(CKSUMLOC)); /* read sum */
sum = (sum & 0xFFFF) + (sum >> 16); /* fold halves */
sum = (sum & 0xFFFF) + (sum >> 16); /* add in carry */
ipcksum = (halfword16)(~(sum & 0xFFFF)); /* 1's negative */
Summary πΈ
This note describes the design of a hardware Internet Checksum that can be implemented in an inexpensive PLD and achieves 1.26 Gbps π. The design has been implemented in the PC-ATOMIC host interface hardware [8]. We believe this design is of general use to the Internet community ππ.
Security Considerations π
Security considerations are not addressed here. The Internet Checksum is not intended as a security measure. π Do not let anyone tell you a checksum is a lock. It is not, and it was never meant to be one π ββοΈ.
Acknowledgements π«Ά
The authors would like to thank the members of the "High-Performance Computing and Communications" group, notably Mike Carlton, and the "Advanced Systems" divisions at ISI π. Thank you for helping with the development of the hardware and of this memo π·.
References π
- [1] Braden, R., Borman, D., and Partridge, C., "Computing the Internet Checksum," Network Working Group RFC-1071, ISI, Cray Research, and BBN Labs, Sept. 1988.
- [2] Mallory, T., and Kullberg, A., "Incremental Updating of the Internet Checksum," Network Working Group RFC-1141, BBN Comm., Jan. 1990.
- [3] Plummer, W., "TCP Checksum Function Design," IEN-45, BBN, 1978, included as an appendix in RFC-1071.
- [4] Postel, Jon, "Internet Protocol," Network Working Group RFC-791/STD-5, ISI, Sept. 1981.
- [5] Postel, Jon, "User Datagram Protocol," Network Working Group RFC-768/STD-6, ISI, Aug. 1980.
- [6] Postel, Jon, "Transmission Control Protocol," Network Working Group RFC-793/STD-7, ISI, Sept. 1981.
- [7] Rijsinghani, A., "Computation of the Internet Checksum via Incremental Update," Network Working Group RFC-1624, Digital Equipment Corp., May 1994.
- [8] Touch, J., "PC-ATOMIC", ISI Tech. Report. SR-95-407, June 1995.
Authors' Addresses π
Joe Touch
University of Southern California/Information Sciences Institute
4676 Admiralty Way
Marina del Rey, CA 90292-6695
USA
Phone: +1 310-822-1511 x151
Fax: +1 310-823-6714
URL: http://www.isi.edu/~touch
EMail: touch@isi.edu
Bruce Parham
University of Southern California/Information Sciences Institute
4676 Admiralty Way
Marina del Rey, CA 90292-6695
USA
Phone: +1 310-822-1511 x101
Fax: +1 310-823-6714
EMail: bparham@isi.edu
Appendix A: PLD source code π§
The following is the PLD source code for an AMD MACH-435 PLD. The MACH-435 is made up of 8 22V10-equivalent PLD blocks, connected by a configurable internal matrix π§©.
---- (PLD source code follows) ----
TITLE PC-ATOMIC IP Sum Accelerator - 1-clock 2- and 3-bit 26 ns version
PATTERN ip_sum
REVISION 1.01
AUTHOR J. Touch & B. Parham
COMPANY USC/ISI
DATE 06/21/94
CHIP ip_sum MACH435
; accumulates in 1 clock (1 level of logic)
;
; resources allocated to reduce fitting time
;
; uses an input register "dl" to latch the data bus values on rising edge
; accumulates a hi/lo ones-complement sum in register "q"
; the input and output are accessed via bidirectional pins "dq"
;
; uses 2 groups of 6 carry bit registers "cy"
;
; use 3-bit full-adders with carry lookahead (settles in 6 clocks)
; group 16 bits as [000102 030405 0607 080910 111213 1415]
; [161718 192021 2223 242526 272829 3031]
;
; locking the pins down speeds up fitting and is designed to force
; 4-bit components into single "segments" of the PLD.
; we could have indicated the same thing via:
; GROUP MACH_SEG_A dq[6..0]
; GROUP MACH_SEG_B dq[14..8]
; GROUP MACH_SEG_C dq[22..16]
; GROUP MACH_SEG_D dq[30..24]
;
; control pins:
;
PIN 20 clk ; adder clock
PIN 62 ip_add ; add current data to sum
PIN 83 ip_sum_ena ; output current sum
PIN 41 ip_clr ; clear current sum
PIN 23 ip_dclk ; input data latch (tied to clk, or not)
;
; dq are data bus pins
; dl is the input register
;
PIN [9..3] dq[6..0] IPAIR dl[6..0] ; IO port
PIN [18..12] dq[14..8] IPAIR dl[14..8] ; IO port
PIN [30..24] dq[22..16] IPAIR dl[22..16] ; IO port
PIN [39..33] dq[30..24] IPAIR dl[30..24] ; IO port
PIN ? dq[31,23,15,7] IPAIR dl[31,23,15,7] ; IO port
;
; q is the partial checksum register
; dl is the input register
; dq are the data bus pins
;
NODE ? q[31..0] OPAIR dq[31..0] ; internal data in reg
NODE ? dl[31..0] REG ; input reg
;
; cy are the carry register bits
;
NODE ? cy[31,29,26,23,21,18,15,13,10,7,5,2] REG
;1-bit internal carry bits
EQUATIONS
;
; .trst is the tri-state control, 0 means these are always inputs
;
ip_add.trst = 0
ip_clr.trst = 0
ip_sum_ena.trst = 0
;
; grab data to the input register on every clock (irrelevant if invalid)
;
dl[31..0].clkf = ip_dclk ; grab data all the time
; don't use setf, rstf, or trst for dl
; we want dl to map to input registers, not internal cells
; besides, input registers don't need setf, rstf, or trst
;
; control of the checksum register
;
dq[31..0].clkf = clk ; clk clocks everything
dq[31..0].setf = gnd ; never preset registers
dq[31..0].rstf = ip_clr ; clear on reset
dq[31..0].trst = ip_sum_ena ; ena outputs sum - read
;
; control for the carry register
;
cy[31,29,26,23,21,18,15,13,10,7,5,2].clkf = clk
cy[31,29,26,23,21,18,15,13,10,7,5,2].setf = gnd ; never preset
cy[31,29,26,23,21,18,15,13,10,7,5,2].rstf = ip_clr ; clear on reset
;
; INPUT DATA LATCH
; nothing fancy here - grab all inputs when ip_add signal is high
; i.e., grab data in input register
;
dl[31..0] := dq[31..0]
;
; COMBINATORIAL ADDER
;
; built as a series of 2-bit and 3-bit (carry-lookahead) full-adders
; with carries sent to the carry register "pipeline"
;
; sum[n] are sum bits
; cy[m] are carry bits
; ":+:" is XOR
;
; SUM[0] = (A0 :+: B0 :+: CARRY_IN)
;
; CY[0] = ((A0 * B0) + ((A0 :+: B0) * CARRY_IN))
;
; actually, the latter can be rewritten as
;
; CY[0] = ((A0 * B0) + ((A0 + B0) * CARRY_IN))
;
; because the XOR won't be invalidated by the AND case, since the
; result is always 1 from the first term then anyway
; this helps reduce the number of XOR terms required, which are
; a limited resource in PLDs
;
; SUM THE LOW-ORDER WORD
;
; the first 5 bits [0..4] of the low-order word
;
q[0] := (q[0] :+: (ip_add * dl[0]) :+: cy[15])
q[1] := (q[1] :+: (ip_add * dl[1]) :+:
((ip_add *
(q[0] * dl[0] +
dl[0] * cy[15])) +
(q[0] * cy[15])))
q[2] := (q[2] :+: (ip_add * dl[2]) :+:
((ip_add *
(q[1] * dl[1] +
q[1] * q[0] * dl[0] +
dl[1] * q[0] * dl[0] +
q[1] * dl[0] * cy[15] +
dl[1] * dl[0] * cy[15] +
dl[1] * q[0] * cy[15])) +
(q[1] * q[0] * cy[15])))
cy[2] := ((ip_add *
(q[2] * dl[2] +
q[2] * q[1] * dl[1] +
dl[2] * q[1] * dl[1] +
q[2] * q[1] * q[0] * dl[0] +
q[2] * dl[1] * q[0] * dl[0] +
dl[2] * q[1] * q[0] * dl[0] +
dl[2] * dl[1] * q[0] * dl[0] +
q[2] * q[1] * dl[0] * cy[15] +
q[2] * dl[1] * q[0] * cy[15] +
q[2] * dl[1] * dl[0] * cy[15] +
dl[2] * q[1] * q[0] * cy[15] +
dl[2] * q[1] * dl[0] * cy[15] +
dl[2] * dl[1] * q[0] * cy[15] +
dl[2] * dl[1] * dl[0] * cy[15])) +
(q[2] * q[1] * q[0] * cy[15]))
q[3] := (q[3] :+: (ip_add * dl[3]) :+: cy[2])
q[4] := (q[4] :+: (ip_add * dl[4]) :+:
((ip_add *
(q[3] * dl[3] +
dl[3] * cy[2])) +
(q[3] * cy[2])))
;
; the next 3 bits [5..7] of the low-order word
;
q[5] := (q[5] :+: (ip_add * dl[5]) :+:
((ip_add *
(q[4] * dl[4] +
q[4] * q[3] * dl[3] +
dl[4] * q[3] * dl[3] +
q[4] * dl[3] * cy[2] +
dl[4] * dl[3] * cy[2] +
dl[4] * q[3] * cy[2])) +
(q[4] * q[3] * cy[2])))
cy[5] := ((ip_add * (
q[5] * dl[5] +
q[5] * q[4] * dl[4] +
dl[5] * q[4] * dl[4] +
q[5] * q[4] * q[3] * dl[3] +
q[5] * dl[4] * q[3] * dl[3] +
dl[5] * q[4] * q[3] * dl[3] +
dl[5] * dl[4] * q[3] * dl[3] +
q[5] * q[4] * dl[3] * cy[2] +
q[5] * dl[4] * q[3] * cy[2] +
q[5] * dl[4] * dl[3] * cy[2] +
dl[5] * q[4] * q[3] * cy[2] +
dl[5] * q[4] * dl[3] * cy[2] +
dl[5] * dl[4] * q[3] * cy[2] +
dl[5] * dl[4] * dl[3] * cy[2])) +
(q[5] * q[4] * q[3] * cy[2]))
q[6] := (q[6] :+: (ip_add * dl[6]) :+: cy[5])
q[7] := (q[7] :+: (ip_add * dl[7]) :+:
((ip_add *
(q[6] * dl[6] +
dl[6] * cy[5])) +
(q[6] * cy[5])))
cy[7] := ((ip_add *
(q[7] * dl[7] +
q[7] * q[6] * dl[6] +
dl[7] * q[6] * dl[6] +
q[7] * dl[6] * cy[5] +
dl[7] * dl[6] * cy[5] +
dl[7] * q[6] * cy[5])) +
(q[7] * q[6] * cy[5]))
;
; the next 5 bits [8..12] of the low-order word
;
q[8] := (q[8] :+: (ip_add * dl[8]) :+: cy[7])
q[9] := (q[9] :+: (ip_add * dl[9]) :+:
((ip_add *
(q[8] * dl[8] +
dl[8] * cy[7])) +
(q[8] * cy[7])))
q[10] := (q[10] :+: (ip_add * dl[10]) :+:
((ip_add *
(q[9] * dl[9] +
q[9] * q[8] * dl[8] +
dl[9] * q[8] * dl[8] +
q[9] * dl[8] * cy[7] +
dl[9] * dl[8] * cy[7] +
dl[9] * q[8] * cy[7])) +
(q[9] * q[8] * cy[7])))
cy[10] := ((ip_add *
(q[10] * dl[10] +
q[10] * q[9] * dl[9] +
dl[10] * q[9] * dl[9] +
q[10] * q[9] * q[8] * dl[8] +
q[10] * dl[9] * q[8] * dl[8] +
dl[10] * q[9] * q[8] * dl[8] +
dl[10] * dl[9] * q[8] * dl[8] +
q[10] * q[9] * dl[8] * cy[7] +
q[10] * dl[9] * q[8] * cy[7] +
q[10] * dl[9] * dl[8] * cy[7] +
dl[10] * q[9] * q[8] * cy[7] +
dl[10] * q[9] * dl[8] * cy[7] +
dl[10] * dl[9] * q[8] * cy[7] +
dl[10] * dl[9] * dl[8] * cy[7])) +
(q[10] * q[9] * q[8] * cy[7]))
q[11] := (q[11] :+: (ip_add * dl[11]) :+: cy[10])
q[12] := (q[12] :+: (ip_add * dl[12]) :+:
((ip_add *
(q[11] * dl[11] +
dl[11] * cy[10])) +
(q[11] * cy[10])))
;
; the final 3 bits [13..15] of the low-order word
;
q[13] := (q[13] :+: (ip_add * dl[13]) :+:
((ip_add *
(q[12] * dl[12] +
q[12] * q[11] * dl[11] +
dl[12] * q[11] * dl[11] +
q[12] * dl[11] * cy[10] +
dl[12] * dl[11] * cy[10] +
dl[12] * q[11] * cy[10])) +
(q[12] * q[11] * cy[10])))
cy[13] := ((ip_add * (
q[13] * dl[13] +
q[13] * q[12] * dl[12] +
dl[13] * q[12] * dl[12] +
q[13] * q[12] * q[11] * dl[11] +
q[13] * dl[12] * q[11] * dl[11] +
dl[13] * q[12] * q[11] * dl[11] +
dl[13] * dl[12] * q[11] * dl[11] +
q[13] * q[12] * dl[11] * cy[10] +
q[13] * dl[12] * q[11] * cy[10] +
q[13] * dl[12] * dl[11] * cy[10] +
dl[13] * q[12] * q[11] * cy[10] +
dl[13] * q[12] * dl[11] * cy[10] +
dl[13] * dl[12] * q[11] * cy[10] +
dl[13] * dl[12] * dl[11] * cy[10])) +
(q[13] * q[12] * q[11] * cy[10]))
q[14] := (q[14] :+: (ip_add * dl[14]) :+: cy[13])
q[15] := (q[15] :+: (ip_add * dl[15]) :+:
((ip_add *
(q[14] * dl[14] +
dl[14] * cy[13])) +
(q[14] * cy[13])))
cy[15] := ((ip_add *
(q[15] * dl[15] +
q[15] * q[14] * dl[14] +
dl[15] * q[14] * dl[14] +
q[15] * dl[14] * cy[13] +
dl[15] * dl[14] * cy[13] +
dl[15] * q[14] * cy[13])) +
(q[15] * q[14] * cy[13]))
; SUM THE HIGH-ORDER WORD
;
okay bestie, we've just hit the high-order word, and it's giving a whole new vibe πβ¨ The same pattern carries on here, just with the next bits in line. Sixteen bits, five at a time, same energy π«Ά
;
; the first 5 bits [16..20] of the high-order word
;
q[16] := (q[16] :+: (ip_add * dl[16]) :+: cy[31])
q[17] := (q[17] :+: (ip_add * dl[17]) :+:
((ip_add *
(q[16] * dl[16] +
dl[16] * cy[31])) +
(q[16] * cy[31])))
q[18] := (q[18] :+: (ip_add * dl[18]) :+:
((ip_add *
(q[17] * dl[17] +
q[17] * q[16] * dl[16] +
dl[17] * q[16] * dl[16] +
q[17] * dl[16] * cy[31] +
dl[17] * dl[16] * cy[31] +
dl[17] * q[16] * cy[31])) +
(q[17] * q[16] * cy[31])))
cy[18] := ((ip_add *
(q[18] * dl[18] +
q[18] * q[17] * dl[17] +
dl[18] * q[17] * dl[17] +
q[18] * q[17] * q[16] * dl[16] +
q[18] * dl[17] * q[16] * dl[16] +
dl[18] * q[17] * q[16] * dl[16] +
dl[18] * dl[17] * q[16] * dl[16] +
q[18] * q[17] * dl[16] * cy[31] +
q[18] * dl[17] * q[16] * cy[31] +
q[18] * dl[17] * dl[16] * cy[31] +
dl[18] * q[17] * q[16] * cy[31] +
dl[18] * q[17] * dl[16] * cy[31] +
dl[18] * dl[17] * q[16] * cy[31] +
dl[18] * dl[17] * dl[16] * cy[31])) +
(q[18] * q[17] * q[16] * cy[31]))
q[19] := (q[19] :+: (ip_add * dl[19]) :+: cy[18])
q[20] := (q[20] :+: (ip_add * dl[20]) :+:
((ip_add *
(q[19] * dl[19] +
dl[19] * cy[18])) +
(q[19] * cy[18])))
;
; the next 3 bits [21..23] of the high-order word
;
q[21] := (q[21] :+: (ip_add * dl[21]) :+:
((ip_add *
(q[20] * dl[20] +
q[20] * q[19] * dl[19] +
dl[20] * q[19] * dl[19] +
q[20] * dl[19] * cy[18] +
dl[20] * dl[19] * cy[18] +
dl[20] * q[19] * cy[18])) +
(q[20] * q[19] * cy[18])))
cy[21] := ((ip_add * (
q[21] * dl[21] +
q[21] * q[20] * dl[20] +
dl[21] * q[20] * dl[20] +
q[21] * q[20] * q[19] * dl[19] +
q[21] * dl[20] * q[19] * dl[19] +
dl[21] * q[20] * q[19] * dl[19] +
dl[21] * dl[20] * q[19] * dl[19] +
q[21] * q[20] * dl[19] * cy[18] +
q[21] * dl[20] * q[19] * cy[18] +
q[21] * dl[20] * dl[19] * cy[18] +
dl[21] * q[20] * q[19] * cy[18] +
dl[21] * q[20] * dl[19] * cy[18] +
dl[21] * dl[20] * q[19] * cy[18] +
dl[21] * dl[20] * dl[19] * cy[18])) +
(q[21] * q[20] * q[19] * cy[18]))
q[22] := (q[22] :+: (ip_add * dl[22]) :+: cy[21])
q[23] := (q[23] :+: (ip_add * dl[23]) :+:
((ip_add *
(q[22] * dl[22] +
dl[22] * cy[21])) +
(q[22] * cy[21])))
cy[23] := ((ip_add *
(q[23] * dl[23] +
q[23] * q[22] * dl[22] +
dl[23] * q[22] * dl[22] +
q[23] * dl[22] * cy[21] +
dl[23] * dl[22] * cy[21] +
dl[23] * q[22] * cy[21])) +
(q[23] * q[22] * cy[21]))
;
; the next 5 bits [24..28] of the high-order word
;
q[24] := (q[24] :+: (ip_add * dl[24]) :+: cy[23])
q[25] := (q[25] :+: (ip_add * dl[25]) :+:
((ip_add *
(q[24] * dl[24] +
dl[24] * cy[23])) +
(q[24] * cy[23])))
q[26] := (q[26] :+: (ip_add * dl[26]) :+:
((ip_add *
(q[25] * dl[25] +
q[25] * q[24] * dl[24] +
dl[25] * q[24] * dl[24] +
q[25] * dl[24] * cy[23] +
dl[25] * dl[24] * cy[23] +
dl[25] * q[24] * cy[23])) +
(q[25] * q[24] * cy[23])))
cy[26] := ((ip_add *
(q[26] * dl[26] +
q[26] * q[25] * dl[25] +
dl[26] * q[25] * dl[25] +
q[26] * q[25] * q[24] * dl[24] +
q[26] * dl[25] * q[24] * dl[24] +
dl[26] * q[25] * q[24] * dl[24] +
dl[26] * dl[25] * q[24] * dl[24] +
q[26] * q[25] * dl[24] * cy[23] +
q[26] * dl[25] * q[24] * cy[23] +
q[26] * dl[25] * dl[24] * cy[23] +
dl[26] * q[25] * q[24] * cy[23] +
dl[26] * q[25] * dl[24] * cy[23] +
dl[26] * dl[25] * q[24] * cy[23] +
dl[26] * dl[25] * dl[24] * cy[23])) +
(q[26] * q[25] * q[24] * cy[23]))
q[27] := (q[27] :+: (ip_add * dl[27]) :+: cy[26])
q[28] := (q[28] :+: (ip_add * dl[28]) :+:
((ip_add *
(q[27] * dl[27] +
dl[27] * cy[26])) +
(q[27] * cy[26])))
;
; the final 3 bits [29..31] of the high-order word
;
q[29] := (q[29] :+: (ip_add * dl[29]) :+:
((ip_add *
(q[28] * dl[28] +
q[28] * q[27] * dl[27] +
dl[28] * q[27] * dl[27] +
q[28] * dl[27] * cy[26] +
dl[28] * dl[27] * cy[26] +
dl[28] * q[27] * cy[26])) +
(q[28] * q[27] * cy[26]))
Okay bestie, the carry chain is basically done π Here's where the last three bits do their thing and the whole multiply gets wrapped up π¦
cy[29] := ((ip_add * (
q[29] * dl[29] +
q[29] * q[28] * dl[28] +
dl[29] * q[28] * dl[28] +
q[29] * q[28] * q[27] * dl[27] +
q[29] * dl[28] * q[27] * dl[27] +
dl[29] * q[28] * q[27] * dl[27] +
dl[29] * dl[28] * q[27] * dl[27] +
q[29] * q[28] * dl[27] * cy[26] +
q[29] * dl[28] * q[27] * cy[26] +
q[29] * dl[28] * dl[27] * cy[26] +
dl[29] * q[28] * q[27] * cy[26] +
dl[29] * q[28] * dl[27] * cy[26] +
dl[29] * dl[28] * q[27] * cy[26] +
dl[29] * dl[28] * dl[27] * cy[26])) +
(q[29] * q[28] * q[27] * cy[26]))
Next up are q[30] and q[31], which are giving main character energy with all those :+: operators πΈ
q[30] := (q[30] :+: (ip_add * dl[30]) :+: cy[29])
q[31] := (q[31] :+: (ip_add * dl[31]) :+:
((ip_add *
(q[30] * dl[30] +
dl[30] * cy[29])) +
(q[30] * cy[29])))
cy[31] := ((ip_add *
(q[31] * dl[31] +
q[31] * q[30] * dl[30] +
dl[31] * q[30] * dl[30] +
q[31] * dl[30] * cy[29] +
dl[31] * dl[30] * cy[29] +
dl[31] * q[30] * cy[29])) +
(q[31] * q[30] * cy[29]))
Now the final step π The nodes get pushed out onto the output pins, and this part is still pending enable, so we're not totally done yet β¨
;
; output nodes onto output pins (pending enable..)
;
dq[0] := {q[0]}
dq[1] := {q[1]}
dq[2] := {q[2]}
dq[3] := {q[3]}
dq[4] := {q[4]}
dq[5] := {q[5]}
dq[6] := {q[6]}
dq[7] := {q[7]}
dq[8] := {q[8]}
dq[9] := {q[9]}
dq[10] := {q[10]}
dq[11] := {q[11]}
dq[12] := {q[12]}
dq[13] := {q[13]}
dq[14] := {q[14]}
dq[15] := {q[15]}
dq[16] := {q[16]}
dq[17] := {q[17]}
dq[18] := {q[18]}
dq[19] := {q[19]}
dq[20] := {q[20]}
dq[21] := {q[21]}
dq[22] := {q[22]}
dq[23] := {q[23]}
dq[24] := {q[24]}
dq[25] := {q[25]}
dq[26] := {q[26]}
dq[27] := {q[27]}
dq[28] := {q[28]}
dq[29] := {q[29]}
dq[30] := {q[30]}
dq[31] := {q[31]}
;
; end.
;
π¦ where this page comes from
- ietf: rfc1936 1996-04
- π girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms