News:

  • October 12, 2025, 09:26:58 PM

Login with username, password and session length

Author Topic: Bit Level Access of UDT Words  (Read 2476 times)

RBPLC

  • Hero Member
  • *****
  • Posts: 585
Bit Level Access of UDT Words
« on: April 18, 2022, 07:26:23 AM »
I would like to write a 1 to MyUDT.dDoubleWord:0. Is this currently possible or will it be down the road? I think I've asked this before but I can't find the post.

franji1

  • Bit Weenie
  • Host Moderator
  • Hero Member
  • *****
  • Posts: 3761
    • Host Engineering
Re: Bit Level Access of UDT Words
« Reply #1 on: April 18, 2022, 09:42:21 AM »
It is available now.  If you understand the concept of C UNIONs and C Bit fields, this will make more sense.

For a good example, go to the Memory Configuration Structs tab.  Look at the Built-in Struct (click the radio button on the right for Built-in Structs).  Double click on Date/Time Struct.

You will notice in the Layout column that there are a number of fields in dw0 and a number of fields in dw1.  Note that the .Date and the .Time fields occupy the WHOLE DWORD they occupy (specifically dw0 and dw1), along with the actual individual date and time fields that share the same dw0 (.Date) and dw1 (.Time).  These occupy THE SAME MEMORY.  If you want to see it graphically, click on the View memory Layout button, you will see this:

Code: [Select]
"Date/Time Struct" Structure Memory Layout (2 total DWORDs in length, DWORD Offset 0..1)
+------++----------------------------------------------------------------------------------------------------------------------------------++
|      || Most Significant                                                                                               Least Significant ||
|      ++----------------------------------------------------------------------------------------------------------------------------------++
|      ||                                                              DWORD                                                               ||
|      ++----------------------------------------------------------------++----------------------------------------------------------------++
|      ||                             WORD 1                             ||                             WORD 0                             ||
|      ++-------------------------------++-------------------------------++-------------------------------++-------------------------------++
|      ||             BYTE 3            ||             BYTE 2            ||             BYTE 1            ||             BYTE 0            ||
|DWORD ++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++
|Offset||b31|b30|b29|b28|b27|b26|b25|b24||b23|b22|b21|b20|b19|b18|b17|b16||b15|b14|b13|b12|b11|b10| b9| b8|| b7| b6| b5| b4| b3| b2| b1| b0||
+------++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++
Note: Every Bit Field layout only shows the Field # (no name).  See the list of Bit Field Names listed by Field # at the end.

+------++----------------------------------------------------------------++-------------------------------++-------------------------------++
|    0 ||   2                         .Year                        UWORD ||   3         .Month      UBYTE ||   4          .Day       UBYTE ||
|      ||----------------------------------------------------------------++-------------------------------++-------------------------------++
|      ||   1                                                          .Date                                                        SDWORD ||
+------++-------------------------------++-------------------------------++-------------------------------++-------------------------------++
|    1 ||   6       .DayOfWeek    UBYTE ||   7         .Hour       UBYTE ||   8        .Minute      UBYTE ||   9        .Second      UBYTE ||
|      ||-------------------------------++-------------------------------++-------------------------------++-------------------------------++
|      ||   5                                                          .Time                                                        SDWORD ||
+------++----------------------------------------------------------------------------------------------------------------------------------++

The first column shows you the actual DWORD OFFSET IN MEMORY.  Notice that .Date occupies the same DWORD as .Year, .Month and .Day.  Similarly, .Time occupies the same DWORD as .DayOfWeek, .Hour, .Minute, and .Second.

A UDT is COMPLETELY CONFIGURABLE (only limitation is that WORDs must be WORD aligned, DWORDs must be DWORD aligned).

So if you go back and look at the Peerlink Struct, those have Bit fields that occupy the same memory as a corresponding WORD.  For example, .B0Act thru .B15Act (***EDIT*** Fields 1 thru Field 16) share the same memory as .Active (Field 17), i.e. the first 16 bits of DWORD offset 0.  The bit fields only have their field # in the layout, they are enumerated at the bottom of the layout.
Code: [Select]
"Peerlink Struct" Structure Memory Layout (7 total DWORDs in length, DWORD Offset 0..6)
+------++----------------------------------------------------------------------------------------------------------------------------------++
|      || Most Significant                                                                                               Least Significant ||
|      ++----------------------------------------------------------------------------------------------------------------------------------++
|      ||                                                              DWORD                                                               ||
|      ++----------------------------------------------------------------++----------------------------------------------------------------++
|      ||                             WORD 1                             ||                             WORD 0                             ||
|      ++-------------------------------++-------------------------------++-------------------------------++-------------------------------++
|      ||             BYTE 3            ||             BYTE 2            ||             BYTE 1            ||             BYTE 0            ||
|DWORD ++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++
|Offset||b31|b30|b29|b28|b27|b26|b25|b24||b23|b22|b21|b20|b19|b18|b17|b16||b15|b14|b13|b12|b11|b10| b9| b8|| b7| b6| b5| b4| b3| b2| b1| b0||
+------++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++
Note: Every Bit Field layout only shows the Field # (no name).  See the list of Bit Field Names listed by Field # at the end.

+------++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++
|    0 || 33| 32| 31| 30| 29| 28| 27| 26|| 25| 24| 23| 22| 21| 20| 19| 18|| 16| 15| 14| 13| 12| 11| 10|  9||  8|  7|  6|  5|  4|  3|  2|  1||
|      ||---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++
|      ||  34                         .Error                       UWORD ||  17                        .Active                       UWORD ||
+------++-------------------------------++-------------------------------++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++
|    1 ||xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx||xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx|| 50| 49| 48| 47| 46| 45| 44| 43|| 42| 41| 40| 39| 38| 37| 36| 35||
|      ||-------------------------------++-------------------------------++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++
|      ||  52                       .MyBlocks                      UWORD ||  51                        .Updated                      UWORD ||
+------++-------------------------------++-------------------------------++-------------------------------++-------------------------------++
|    2 ||  56        .B3Rate      UBYTE ||  55        .B2Rate      UBYTE ||  54        .B1Rate      UBYTE ||  53        .B0Rate      UBYTE ||
+------++-------------------------------++-------------------------------++-------------------------------++-------------------------------++
|    3 ||  60        .B7Rate      UBYTE ||  59        .B6Rate      UBYTE ||  58        .B5Rate      UBYTE ||  57        .B4Rate      UBYTE ||
+------++-------------------------------++-------------------------------++-------------------------------++-------------------------------++
|    4 ||  64        .B11Rate     UBYTE ||  63        .B10Rate     UBYTE ||  62        .B9Rate      UBYTE ||  61        .B8Rate      UBYTE ||
+------++-------------------------------++-------------------------------++-------------------------------++-------------------------------++
|    5 ||  68        .B15Rate     UBYTE ||  67        .B14Rate     UBYTE ||  66        .B13Rate     UBYTE ||  65        .B12Rate     UBYTE ||
+------++-------------------------------++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++
|    6 ||xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx||xxx|xxx|xxx|xxx|xxx|xxx|xxx| 87|| 84| 83| 82| 81| 80| 79| 78| 77|| 76| 75| 74| 73| 72| 71| 70| 69||
|      ||-------------------------------++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++---+---+---+---+---+---+---+---++
|      ||xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx||  85                        .Inhibit                      UWORD ||
+------++----------------------------------------------------------------++----------------------------------------------------------------++

List of Bit Fields by Field #
  #1 (dw 0: 0)  .B0Act
  #2 (dw 0: 1)  .B1Act
  #3 (dw 0: 2)  .B2Act
  #4 (dw 0: 3)  .B3Act
  #5 (dw 0: 4)  .B4Act
  #6 (dw 0: 5)  .B5Act
  #7 (dw 0: 6)  .B6Act
  #8 (dw 0: 7)  .B7Act
  #9 (dw 0: 8)  .B8Act
 #10 (dw 0: 9)  .B9Act
 #11 (dw 0:10)  .B10Act
 #12 (dw 0:11)  .B11Act
 #13 (dw 0:12)  .B12Act
 #14 (dw 0:13)  .B13Act
 #15 (dw 0:14)  .B14Act
 #16 (dw 0:15)  .B15Act
 #18 (dw 0:16)  .B0Err
 #19 (dw 0:17)  .B1Err
 #20 (dw 0:18)  .B2Err
 #21 (dw 0:19)  .B3Err
 #22 (dw 0:20)  .B4Err
 #23 (dw 0:21)  .B5Err
 #24 (dw 0:22)  .B6Err
 #25 (dw 0:23)  .B7Err
 #26 (dw 0:24)  .B8Err
 #27 (dw 0:25)  .B9Err
 #28 (dw 0:26)  .B10Err
 #29 (dw 0:27)  .B11Err
 #30 (dw 0:28)  .B12Err
 #31 (dw 0:29)  .B13Err
 #32 (dw 0:30)  .B14Err
 #33 (dw 0:31)  .B15Err
 #35 (dw 1: 0)  .B0Updated
 #36 (dw 1: 1)  .B1Updated
 #37 (dw 1: 2)  .B2Updated
 #38 (dw 1: 3)  .B3Updated
 #39 (dw 1: 4)  .B4Updated
 #40 (dw 1: 5)  .B5Updated
 #41 (dw 1: 6)  .B6Updated
 #42 (dw 1: 7)  .B7Updated
 #43 (dw 1: 8)  .B8Updated
 #44 (dw 1: 9)  .B9Updated
 #45 (dw 1:10)  .B10Updated
 #46 (dw 1:11)  .B11Updated
 #47 (dw 1:12)  .B12Updated
 #48 (dw 1:13)  .B13Updated
 #49 (dw 1:14)  .B14Updated
 #50 (dw 1:15)  .B15Updated
 #69 (dw 6: 0)  .B0Inh
 #70 (dw 6: 1)  .B1Inh
 #71 (dw 6: 2)  .B2Inh
 #72 (dw 6: 3)  .B3Inh
 #73 (dw 6: 4)  .B4Inh
 #74 (dw 6: 5)  .B5Inh
 #75 (dw 6: 6)  .B6Inh
 #76 (dw 6: 7)  .B7Inh
 #77 (dw 6: 8)  .B8Inh
 #78 (dw 6: 9)  .B9Inh
 #79 (dw 6:10)  .B10Inh
 #80 (dw 6:11)  .B11Inh
 #81 (dw 6:12)  .B12Inh
 #82 (dw 6:13)  .B13Inh
 #83 (dw 6:14)  .B14Inh
 #84 (dw 6:15)  .B15Inh
 #87 (dw 6:16)  .Enabled

The way you manually layout bit fields in your UDT, is bring up your UDT Editor, figure out
1. What DWORD OFFSET you want THAT BIT field to occupy
2. What BIT # (0..31) within THAT specific DWORD OFFSET

then
3. Hit the Add or Insert Field button
4. Give the Field a Name, select the Data type (default is Bit), fill in all the other fields
5. This is the important step - in the Memory Layout (bottom right), select Manual and choose the DWORD OFFSET and Bit Offset you want THAT SPECIFIC BIT.

Hit OK and then look at the new field in the UDT field list.  Look at the Layout column to see where it's layed out.  Click on the Layout column to sort by the DWORD/OFFSET (vs default Field# - note that the Field# and the Layout do NOT CORRESPOND - field numbers just get added or inserted).  Hit the View Memory Layout to see it visually.

Again, if you are familiar with C Unions, especially bit field in C Unions, this would make more sense.  If not, the manual Memory Layout mechanism is first DWORD OFFSET THEN the corresponding DWORD Bit# (0-31), or DWORD Byte# (0-3), or DWORD Word # (0-1) (REAL/DWORD MUST be DWORD aligned, so they do not need the 2nd "offset within DWORD" value, it's always 0).

PLEASE ask questions here.  Other people definitely want to do the same thing you are trying to do (e.g. cast a specific bit of an existing WORD field, or look at a group of BITs as a BYTE or WORD or DWORD)  You need to create a bit field that occupies the same bit (see PeerLink Struct - it has multiple WORD fields utilizing bit fields)

Definitely ask questions, and even describe in detail how you would want it layed out (you can EXPORT your UDT definition - others can IMPORT, TWEAK IT EXPORT THAT one, and RE-POST).
« Last Edit: April 18, 2022, 09:45:50 AM by franji1 »

RBPLC

  • Hero Member
  • *****
  • Posts: 585
Re: Bit Level Access of UDT Words
« Reply #2 on: April 18, 2022, 11:12:03 AM »
If I understand correctly, in order to write a 1 to MyUDT.dDoubleWord:0, I have to manually go in and configure bits to be aligned with dDoubleWord and then I can access those bits? 

franji1

  • Bit Weenie
  • Host Moderator
  • Hero Member
  • *****
  • Posts: 3761
    • Host Engineering
Re: Bit Level Access of UDT Words
« Reply #3 on: April 18, 2022, 11:29:46 AM »
If I understand correctly, in order to write a 1 to MyUDT.dDoubleWord:0, I have to manually go in and configure bits to be aligned with dDoubleWord and then I can access those bits?
Yes.  There is no mechanism for casting a field of a structure.  Hence, you must actually create fields that occupy the same memory.

An alternative is to copy the field value into a D, manipulate the bit in the D using a cast, then copy the D back on top of the field.  Otherwise, you have to utilize the C union/bit field type of mechanism.

BobO

  • Host Moderator
  • Hero Member
  • *****
  • Posts: 6126
  • Yes Pinky, Do-more will control the world!
Re: Bit Level Access of UDT Words
« Reply #4 on: April 18, 2022, 11:38:08 AM »
If I understand correctly, in order to write a 1 to MyUDT.dDoubleWord:0, I have to manually go in and configure bits to be aligned with dDoubleWord and then I can access those bits?

Struct fields are effectively casts themselves. They both use the same storage, so it isn't possible to cast a field. Unions give you the same result, but have advantages of their own. You might have a WORD field .Flags that accesses all, and then individual named bit fields for each flag, e.g., .Start, .End, etc. Makes for more readable code than .Flags:0, .Flags:1, etc.
"It has recently come to our attention that users spend 95% of their time using 5% of the available features. That might be relevant." -BobO

RBPLC

  • Hero Member
  • *****
  • Posts: 585
Re: Bit Level Access of UDT Words
« Reply #5 on: April 18, 2022, 01:51:56 PM »
This isn't as bad as I thought after thinking about it. I've already created the bits, I just need overlay the dword. I can't check right now, but I'm not sure the bits of interest are aligned on a dword. I would like them to be so that they are "segregated" and the dword isn't overlapping in a non-functionally related memory area. If I move these bits to get them dword aligned, how bad does this mess up the program? What do I need to watch out for?

franji1

  • Bit Weenie
  • Host Moderator
  • Hero Member
  • *****
  • Posts: 3761
    • Host Engineering
Re: Bit Level Access of UDT Words
« Reply #6 on: April 18, 2022, 02:17:51 PM »
This isn't as bad as I thought after thinking about it. I've already created the bits, I just need overlay the dword. I can't check right now, but I'm not sure the bits of interest are aligned on a dword. I would like them to be so that they are "segregated" and the dword isn't overlapping in a non-functionally related memory area. If I move these bits to get them dword aligned, how bad does this mess up the program? What do I need to watch out for?

Should work fine.  Even any C-more that utilizes the Symbolic Driver, the PLC will adjust accordingly.

In microprocessor world, it's common to put "status" bits in one byte and "control" bits in a separate byte.  You typically do not intermix status and control bits in the same (byte/word/dword) register.  If you have more than 8 of one, use a WORD instead of a BYTE.  More than 16 of one, use a DWORD.

For example, in the Peerlink Struct, the Inhibit bits are in their own WORD.  These are Control bits and can be set/written by the PLC logic.  The Rate Updated Active and Error bits are Status bits, and occupy their own WORDs.  Block 0 Inhibit is not in the same WORD as Block 0 Error, because the former is a Control bit, and the latter is a Status bit.  You would typically have low level logic writing to the Status bits (think device driver like Peerlink firmware behavior writing to the Error bits), and higher level logic monitoring the Status bits (e.g. when Block 0 is in Error, .B0Err, send an email), or even better, when .Error is non-zero (i.e. at least one of the 16 block error bits is ON), send an email that contains the .Error value in binary or hexadecimal - or even crack the text using the individual error bits, but used the .Error Unsigned Word in a relational contact to drive the EMAIL instruction (see attached screen shot).


RBPLC

  • Hero Member
  • *****
  • Posts: 585
Re: Bit Level Access of UDT Words
« Reply #7 on: April 19, 2022, 07:58:11 AM »
Initially, after asking the question yesterday, I thought that overlaying the DWORD was kind of tedious and a convoluted way of getting to what I was trying to do. After thinking about it more and actually doing it, I see the utility in it. By overlaying the bits with a DWORD you get both the bits and the status word at the same time. This actually helps when coding as then you are not having to write to both a bit and bit of word location in ladder. With a little pre-planning, making sure everything is dword aligned, you could easily build control/status words to match bits. I guess you don't know what you don't know.   

franji1

  • Bit Weenie
  • Host Moderator
  • Hero Member
  • *****
  • Posts: 3761
    • Host Engineering
Re: Bit Level Access of UDT Words
« Reply #8 on: April 19, 2022, 09:00:13 AM »
We can't take any credit for any of this.  This has been done in the microprocessor world for a long time.  Microprocessors are great at accessing BYTEs.  Bits, not so much.  But you can store a lot of data in bits (8 in a BYTE), and move BYTEs around much faster than Bits (this is one reason why GPUs are great - they can manipulate BITs as well as BYTEs).

So, which is faster?
MEMCOPY 64 bits X0 (thru X63) to C0 (thru C63) (that's a lot of BYTE read/modify/writes, tweaking bits at BYTE based memory access)
MEMCOPY 2 DWORDs X0:SD (thru X32:SD) to C0:SD (thru C32:SD) (DWORD aligned DWORD copy - very fast, just a few CPU clock cycles).

If you have a bunch of these, your PLC scan times can vary greatly (former is slow, latter is much faster) just by aligning the bit copies on to BYTE/WORD/DWORD boundaries.


MAEdwards

  • Sr. Member
  • ****
  • Posts: 56
Re: Bit Level Access of UDT Words
« Reply #9 on: April 19, 2022, 01:18:14 PM »
Great information.  Thank you, HOST!