Omron CJ1 DM Memory: 16-bit Words and Byte Operations

James Nishida13 min read
CJ/CP SeriesOmronTechnical Reference
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Omron CJ1 DM Memory: 16-bit Words and Byte Operations

This reference covers the architectural reason Omron CJ-series DM areas are organized as 16-bit words rather than byte-addressable memory, the practical workarounds for byte-level operations using the ANDW instruction, and serial-communication byte-packing techniques for TXD and RXD instructions. All examples target CX-Programmer (CX-One) version 9.x or later.

Overview: The 16-bit Word Memory Model

Omron CJ1 and CJ2 PLCs organize all I/O, work, and data memory in 16-bit words. The DM (Data Memory) area is no exception: each DM location such as D0, D1, or D1000 is a 16-bit word, not a single byte. Attempting to address a single byte using a syntax like D0.0 (low byte) or D0.1 (high byte) is not valid in the CJ1 or CJ2 instruction set. The smallest addressable unit within a DM word is the bit, written as D0.00 through D0.15.

This architectural decision traces back to the Omron V8 PLC introduced in 1978, and it has been preserved through the C-series, CV-series, CS1, CJ1, and CJ2 product lines. The motivation is twofold: bus efficiency (one 16-bit fetch retrieves two bytes simultaneously) and instruction-set simplicity (a single word-oriented ALU handles most arithmetic and logical operations). Modern CJ-series CPUs operate with abundant memory, but the word-oriented model remains for backward compatibility and execution speed.

DM Word Structure

D1000 (16-bit word) High Byte (bits 15-8) Low Byte (bits 7-0) Mask: #FF00 Mask: #00FF

Why 16-bit Words Instead of Bytes

Three engineering reasons drive the 16-bit organization in Omron PLCs:

  1. Bus width alignment: The internal data bus in CJ-series CPUs is 16 bits wide. A word-sized fetch completes in one bus cycle; an 8-bit fetch would either waste bus capacity or require additional byte-enable logic.
  2. Instruction execution speed: Word-oriented arithmetic (ADD, SUB, MUL, DIV) and logical operations (ANDW, ORW, XORW) operate on 16-bit data in a single CPU cycle. Byte-masked versions would either operate on the same 16-bit word internally or require slower microcoded routines.
  3. Backward compatibility: Every Omron PLC since 1978 has used the word-oriented model. Changing the addressing scheme would break the installed base of programs in long-life applications such as utilities, water/wastewater, and process control.

For serial communication applications where individual bytes must be transmitted or received, the word-oriented model introduces an impedance mismatch. The TXD (Transmit) and RXD (Receive) instructions in CJ-series PLCs expect word-sized data; transmitting one byte at a time requires packing two bytes into a single DM word and managing the high-byte/low-byte boundary manually.

DM Memory Map in CJ1 and CJ2 CPUs

The DM area in CJ-series PLCs is a contiguous block of 16-bit words, accessible through the D prefix in CX-Programmer. The address range and capacity depend on the CPU model:

CPU Model DM Area Range Word Count Retentive
CJ1M-CPU11/12/13 D0 to D32767 32,768 Yes (battery)
CJ1M-CPU21/22/23 D0 to D32767 32,768 Yes (battery)
CJ1G-CPU42/43/44/45 D0 to D32767 32,768 Yes (battery)
CJ1H-CPU65/66/67/68 D0 to D32767 32,768 Yes (battery)
CJ2M-CPU11/12/13/14/15 D0 to D32767 32,768 Yes (battery)
CJ2H-CPU64/65/66/67/68 D0 to D32767 32,768 Yes (battery)

Extended Memory (EM) banks are available in some CJ2 CPUs and provide additional word-addressable storage in 32K-word banks. EM banks use the same 16-bit word organization as DM and are addressed with the E prefix (e.g., E0_0, E0_1).

Bit-level addressing within DM is supported using the dot notation: D0.00 through D0.15. This is the only sub-word addressing built into the CJ1/CJ2 instruction set. Byte addressing is not natively supported.

Bit-Level Access: D0.00 to D0.15

While byte access is not available, bit access within a DM word is fully supported. The notation D0.00 refers to the least-significant bit (LSB) of word D0; D0.15 refers to the most-significant bit (MSB). The following example demonstrates setting and clearing individual bits in a ladder program:

LD    P_On
OR    D100.00
ANDNOT D100.01
OUT   D100.00    ; Set bit 0 of D100
OUT   D100.15    ; Set bit 15 of D100

Bit access is useful for flag storage, individual status bits, and boolean memory locations. It is not, however, a substitute for byte access in serial communication buffers, ASCII storage, or any application that requires 8-bit data integrity.

Word-to-Byte Workaround: ANDW Masking

The standard technique for extracting or isolating a single byte from a 16-bit DM word is the ANDW (AND Word) instruction. The format is:

ANDW  S1  S2  D

Where S1 and S2 are the source words to be ANDed, and D is the destination word. The result is the bitwise AND of S1 and S2 stored in D. To use a constant mask, prefix the value with # for hexadecimal or & for decimal.

Isolating the Low Byte (bits 0-7)

To extract the low byte of D1000 and place it in D500:

ANDW  D1000  #00FF  D500

The mask #00FF (hexadecimal 0x00FF) clears bits 8-15 and preserves bits 0-7. The result in D500 contains the low byte in the lower 8 bits and 0x00 in the upper 8 bits.

Isolating the High Byte (bits 8-15)

To extract the high byte of D1000 and place it in D500:

ANDW  D1000  #FF00  D500

The mask #FF00 (hexadecimal 0xFF00) clears bits 0-7 and preserves bits 8-15. The result in D500 contains the high byte in the upper 8 bits and 0x00 in the lower 8 bits.

Shifting the High Byte to the Low Byte Position

To shift the high byte into the low byte position (so the byte can be transmitted or compared as an 8-bit value), use either division or rotation:

Method 1: Division

ANDW  D1000  #FF00  D500   ; Mask to high byte only (result: 0xHH00)
DIV   D500   #0100   D502  ; Divide by 256, quotient in D502
; D502 now contains 0x00HH (high byte shifted to low byte position)

Method 2: Eight rotations

ANDW  D1000  #FF00  D500   ; Mask to high byte only
ROR   D500               ; Rotate right by 1 bit
ROR   D500
ROR   D500
ROR   D500
ROR   D500
ROR   D500
ROR   D500
ROR   D500               ; After 8 rotations, high byte is in low byte position
The ROR (Rotate Right) instruction in CJ-series PLCs operates on a 16-bit word and rotates all 16 bits by one position per execution. Division by 256 is faster (single instruction) but produces a remainder word that must be ignored. The rotation method is more explicit and easier to troubleshoot in field service.

Practical Example: TXD/RXD Serial Byte Packing

The most common scenario requiring byte access in a CJ1 or CJ2 is serial communication using the TXD (Transmit) and RXD (Receive) instructions. These instructions transfer 16-bit words; if the connected device sends or expects 8-bit data, the program must pack or unpack bytes manually.

Scenario

A CJ1M-CPU12 communicates with an RFID reader over RS-232 at 9600 baud, 8 data bits, no parity, 1 stop bit. The reader sends ASCII command responses terminated by CR (0x0D). The PLC must receive the string and parse it character by character.

Receive Unpack Method

  1. RXD instruction places a 16-bit word into D2000. Two ASCII characters are packed into this word: the first character in the low byte, the second in the high byte.
  2. Extract character 1 (low byte): ANDW D2000 #00FF D2010
  3. Extract character 2 (high byte): ANDW D2000 #FF00 D2020 followed by DIV D2020 #0100 D2021 to shift it to the low byte position.
  4. Increment the receive counter and continue until the terminator (0x0D) is detected.

Transmit Pack Method

To pack two ASCII characters from D3000 (low byte) and D3001 (low byte) into a single word D3010 for transmission:

ANDW  D3000   #00FF   D3020   ; Mask D3000 low byte
MUL   D3020   #0100   D3020   ; Shift left 8 positions (multiply by 256)
ANDW  D3001   #00FF   D3030   ; Mask D3001 low byte
ORW   D3020   D3030   D3010   ; OR the two halves together
TXD   D3010   D3011   #0001   ; Transmit 1 word

The TXD instruction control word format (D3011) depends on the serial port configuration and CX-Programmer version. For RS-232C port 1 on CJ1M-CPU12, a typical control word is #0001 (start transmission, 1 word). Refer to the CX-Programmer Operation Manual for the exact control word format for your serial port configuration.

Alternative Methods: Stack Pointers and Index Registers

For applications with thousands of bytes (barcode data, long ASCII strings, file transfers), a stack-pointer approach is more maintainable than per-byte mask-and-store:

; Receive loop using index register IR0
LD    RXD_Complete
MOV   #0 IR0
LOOP:
ANDW  D_RXDBuf[IR0]  #00FF  D_String[IR0]
; D_RXDBuf is the RXD buffer; D_String is the parsed string storage
INC   IR0
CMP   IR0  #1000
BL    LOOP

This method wastes the high byte of every word in D_String (50% memory inefficiency) but uses only one ANDW operation per byte, avoiding rotation overhead. For a 1000-byte string, this consumes 1000 words of DM versus the 500-word consumption of a packed scheme, but the code is significantly simpler and easier to maintain.

For packed storage using an index step of 2:

; Pack two bytes into one word using IR0 with step 2
LD    ByteReady
ANDW  D_SourceA  #00FF  D_Temp
MUL   D_Temp    #0100   D_Temp
ANDW  D_SourceB  #00FF  D_Temp2
ORW   D_Temp    D_Temp2  D_Packed[IR0]
ADD   IR0       #2

This reduces DM consumption by 50% but adds four instructions per byte. Choose packed storage when DM space is critical; choose unpacked storage when scan time and code clarity are priorities.

Comparison: Byte Access Across PLC Platforms

Platform Smallest Addressable Unit Byte Access Syntax Workaround Required
Omron CJ1/CJ2 Bit (D0.00) Not native ANDW with mask
Siemens S7-200 Byte (VB100) VB100, MB0 None
Siemens S7-1200/S7-1500 Byte (DB1.DBB0) DB1.DBB0, MB0 None
Siemens S5 (legacy) Byte (FY0) FY0, MB0 None
Allen-Bradley SLC 500 Bit (B3:0/0) Not native MVM (Masked Move)
Allen-Bradley MicroLogix Bit (B3:0/0) Not native MVM (Masked Move)
Allen-Bradley ControlLogix Bit (tag.0) Direct: tag.0 None for SINT type
Mitsubishi FX Series Bit (M0) Limited BMOV or MOV with mask
Mitsubishi Q/L Series Bit (M0) Limited BMOV or MOV with mask
Schneider Modicon M340 Byte (%MB0) %MB0, %MW0.X None

Omron's approach is consistent with the older C-series heritage. Allen-Bradley's SLC 500 and MicroLogix lines have the same constraint; ControlLogix's tag-based model provides more flexibility through SINT (8-bit) data types. Siemens S7 platforms offer the most native byte access across all product generations.

Troubleshooting Matrix: Common Byte-Access Issues

Symptom Likely Cause Diagnostic Step Fix
Received data shifted by 1 byte High-byte/low-byte packing reversed Force D_RXDBuf to 0x4142, verify D2010 = 0x42 (B), D2021 = 0x41 (A) Swap mask: use #00FF for first char, #FF00 for second
Transmitted data has extra zero bytes High byte not masked before TXD Watch D3010 in online mode; verify upper 8 bits = 0 Add ANDW D3010 #00FF D3010 before TXD
ASCII string terminator not detected Comparison uses 16-bit value instead of 8-bit Watch terminator value; verify = 0x000D after mask Always apply ANDW #00FF before CMP with 0x0D
Loop overruns DM boundary Index register not initialized Monitor IR0; verify starts at 0 Add MOV #0 IR0 at scan start or first scan flag
Garbled data on RS-485 multi-drop Byte packing across DM word boundary Verify two-byte pair stays within one word; do not split pairs Use even index values for packed storage
Scan time increases significantly Rotation loop executed per byte Profile scan time with CX-Programmer trace Replace ROR x 8 with single DIV #0100

Verification and Field Testing

After implementing byte-access logic in a CJ1/CJ2 program, verify the following using CX-Programmer (version 9.x or later recommended):

  1. Online monitoring: Place the ANDW instruction on a watch window. Force D1000 to a known value (e.g., 16#1234). Verify that D500 shows 16#1200 after the high-byte mask and 16#0034 after the low-byte mask.
  2. RXD capture test: Connect a terminal emulator to the serial port. Send a known string. Verify that the unpacked bytes match the transmitted characters by comparing to a reference table.
  3. Loop counter check: For index-register-based loops, verify that IR0 increments correctly and terminates at the expected count. Add a breakpoint or watch window entry for IR0.
  4. High-byte zero check: After every low-byte extraction, verify that the high byte of the destination word is 0x00. Any non-zero value indicates a masking error that will corrupt downstream processing.
  5. Loopback test: Connect a wire from TX to RX on the serial port. Transmit a known pattern; verify the received pattern matches byte-for-byte.
Always test byte-packing logic with a known reference pattern (e.g., 0xAA55) before deploying to production. A single masking error can corrupt an entire serial communication stream silently and only become apparent when the receiving device rejects the data or returns error codes.

Edge Cases: When Byte Access Matters Most

In most modern applications, the word-oriented model of Omron CJ-series PLCs is invisible to the programmer. Three edge cases make byte access relevant:

  1. Serial protocols with 8-bit data fields: Modbus RTU, ASCII protocols, and many RFID and barcode readers transmit 8-bit data. The TXD/RXD packing overhead is unavoidable. For Modbus RTU specifically, the CJ1W-SCU or CJ2M built-in serial port can be configured for protocol macro mode to handle byte-packing automatically, reducing programmer burden.
  2. Ethernet/IP explicit messaging with byte arrays: Some CIP (Common Industrial Protocol) assemblies use 8-bit SINT elements. The CJ2H-CPU6x-EIP implementation requires manual byte packing into INT (16-bit) arrays when transferring data to or from DM memory.
  3. String handling: Omron STRING data type in CX-Programmer is byte-addressable internally and tracks length in the first word. Conversion to or from DM words for storage or transmission requires length tracking plus per-byte masking as described in this reference.

For all three cases, the ANDW plus rotation or ANDW plus stack-pointer methods described earlier provide workable solutions. The choice depends on whether memory efficiency (packed, rotation method) or scan time (unpacked, index-pointer method) is the priority for the specific application.

FAQ

Can I address a single byte in an Omron CJ1 DM word directly?

No. The CJ1 and CJ2 instruction set does not support direct byte addressing in DM (or any other) memory. The smallest addressable unit is the bit, written as D0.00 through D0.15. To work with individual bytes, use the ANDW instruction with a hex mask (0x00FF for low byte, 0xFF00 for high byte) or a stack-pointer loop with index register IR0.

What is the difference between ANDW and the MVM instruction in Allen-Bradley?

ANDW in Omron performs a bitwise AND of two 16-bit words and stores the result in a destination word. MVM (Masked Move) in Allen-Bradley SLC 500 and MicroLogix performs a similar operation but can selectively pass or block bits in a single source word, with the mask and source combined via AND and the result ORed into the destination. Both serve the same purpose for byte isolation; the choice depends on which platform is in use.

How do I send a single ASCII character over RS-232 from a CJ1M-CPU12?

Use the TXD instruction with a DM word containing the ASCII character in the low byte and 0x00 in the high byte. For example, to send 'A' (0x41): MOV 16#0041 D2000, then TXD D2000 D2001 #0001. The receiving device will see the byte 0x41 regardless of the high-byte zero padding. Set the serial port to 8 data bits, no parity, 1 stop bit in the PLC Setup area or in CX-Programmer's Serial Communications dialog.

Does the CJ2 series change the memory architecture from CJ1?

No. The CJ2 series (CJ2H, CJ2M) maintains the same 16-bit word organization for DM, EM, CIO, WR, HR, and AR memory areas. The CJ2 adds more memory capacity, faster scan times, and new instructions (including function-block structured text) but the underlying word-based addressing is unchanged. Programs written for CJ1 typically transfer to CJ2 with little or no modification.

Is there a byte data type in CX-Programmer for CJ1 or CJ2?

CX-Programmer (CX-One version 4.0 or later) supports STRING data types that are byte-addressable internally and track length in the first word. However, when STRING data is moved to DM memory for storage or transmission, it is still stored as 16-bit words with the same packing considerations. For pure byte arrays in DM, the ANDW masking or stack-pointer techniques described in this reference are required.

Back to blog