From bit position to array index - Page 1
Forum section: electronics / beginners

author: Moriambar
author_rank: Unknown
timestamp: October 11, 2020, 07:17:31 pm
content: Hi,I currently am programming an atmega328 and I have a "simple" task:I may only have one pin on PORTB active. The "position" of the "active" pin should translate into an array index.Eg.if the value is 0b00000001 I have to have 0if the value is 0b00000010 I need 1if the value is 0b00000100 I need 2same same is 0b00001000 =>3and so on.I found an ugly way to do this (and it works) but it is a series of ifs basically. Is there any better way to do this?Thanks

author: ataradov
author_rank: Super Contributor
timestamp: October 11, 2020, 08:00:12 pm
content: Code:uint8_t index = 0;for (index = 0; index < 8; index++)if (value & (1 << index))break;index will correspond to your array index. There are many ways to optimize this depending on what you need more speed or small code size.

author: Moriambar
author_rank: Unknown
timestamp: October 11, 2020, 08:14:43 pm
content: Quote from: ataradov on October 11, 2020, 08:00:12 pmthank you.This will do.

author: brucehoult
author_rank: Super Contributor
timestamp: October 11, 2020, 11:06:43 pm
content: You use C's left shift operator to move a single 1 bit left by the number of places you need for your index.char mask = 1 << index;You can then use mask to set, clear, or test one bit in the port. Presumably you already have that part working.There is also a _BV(index) macro that the AVR libraries define (and seems to be standard on most microcontrollers?) that does the same thing for you. I think it stands for "Bit Value"? Dunno.char mask = _BV(index);

author: David Hess
author_rank: Super Contributor
timestamp: October 12, 2020, 01:09:20 am
content: In logic this would be done with a priority encoder, but offhand I do not know the best way to implement it with a processor. Some instruction sets include an instructions to count bits which suggests inverting if necessary and then counting least significant bits.

author: brucehoult
author_rank: Super Contributor
timestamp: October 12, 2020, 01:44:49 am
content: Oh, wait ... you're trying to go FROM a bit mask TO the bit position?That's ... highly unusual.Are you really really sure there is always and only exactly one bit set? That seems highly unlikely, unless things started off as a 3 bit signal which something then decoded into a 1-of-8. But in that case, why not just input the 3 bit signal directly?What do you want to happen if there is *not* exactly one bit set? Maybe that "can never happen", but you should still decide anyway.ataradov's code will find the first (rightmost) bit that is set.If you want to get an error if no bits are set or if more than one bit is set then could modify it to:Code:int whichBitSet(uint8_t value){for (int index = 0; index < 8; index++)if (value == (1 << index))return index;return -1; // error}If you want the fastest possible code then make an array with 256 elements.Code:#include <avr/pgmspace.h>const int8_t bitSetArray[] PROGMEM ={-1, 0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1,4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};int whichBitSet(uint8_t value){return bitSetArray;}This would be very slightly faster without the "PROGMEM", but you can probably afford 256 bytes of flash more than you can afford 256 bytes of SRAM.

author: RenThraysk
author_rank: Regular Contributor
timestamp: October 12, 2020, 02:09:41 am
content: If looking for the index of the most significant bit set, this is a log2operation.While there are some interesting ways to calculate on wider CPUs (if they don't have specific assembly instructions) using De Bruijn sequences, suspect on 8 bit is simple loop or lookup table is the way to go.

author: ledtester
author_rank: Super Contributor
timestamp: October 12, 2020, 03:14:04 am
content: Quote from: brucehoult on October 12, 2020, 01:44:49 amEdit: Never mind... didn't read the question correctly.That array doesn't look right -- at least not to find the first bit set.I think it should look something like:Code:const int8_t bitSetArray[] PROGMEM = {-1, 0, 1, 0, 2, 0, 1, 0, // 0 - 73, 0, 1, 0, 2, 0, 1, 0,4, 0, 1, 0, 2, 0, 1, 0,3, 0, 1, 0, 2, 0, 1, 0,5, 0, 1, 0, 2, 0, 1, 0,3, 0, 1, 0, 2, 0, 1, 0,4, 0, 1, 0, 2, 0, 1, 0,3, 0, 1, 0, 2, 0, 1, 0,5, 0, 1, 0, 2, 0, 1, 0,...}

author: brucehoult
author_rank: Super Contributor
timestamp: October 12, 2020, 04:32:30 am
content: Quote from: ledtester on October 12, 2020, 03:14:04 amThat's because it's to find the ONLY bit set and error if there is not exactly one of them, to fit the original poster's apparent requirements.As discussed at length in the prose above the code.

author: HwAoRrDk
author_rank: Super Contributor
timestamp: October 12, 2020, 05:04:36 am
content: As it's AVR, you also could use a GCC built-in function to do this in one line of code, with__builtin_ffs.Code:int index = __builtin_ffs(PORTB) - 1;Index will be -1 if no bit is set.

author: dolbeau
author_rank: Regular Contributor
timestamp: October 12, 2020, 06:47:33 am
content: Quote from: Moriambar on October 11, 2020, 07:17:31 pmThis is similar to counting leading zeroes - the value you search being (word_width - (1+number_of_leading_zeroes)). For high-level code, that's a common algorithm that is documented inHacker's Delight section 5-3.It's a good book to own if you're often looking for efficient way of working with integers.

author: brucehoult
author_rank: Super Contributor
timestamp: October 12, 2020, 08:20:18 am
content: Quote from: HwAoRrDk on October 12, 2020, 05:04:36 amOne line of code in your program, but a lot of instructions"Code:0000077c <__ffshi2>:77c: aa 27 eor r26, r2677e: 81 11 cpse r24, r1780: 0c 94 c6 03 jmp 0x78c ; 0x78c <__loop_ffsqi2>784: a8 e0 ldi r26, 0x08 ; 8786: 89 2b or r24, r25788: d9 f7 brne .-10 ; 0x780 <__ffshi2+0x4>78a: 08 95 ret0000078c <__loop_ffsqi2>:78c: a3 95 inc r2678e: 86 95 lsr r24790: e8 f7 brcc .-6 ; 0x78c <__loop_ffsqi2>792: 8a 2f mov r24, r26794: 99 27 eor r25, r25796: 08 95 retIf the argument is zero then it returns 8, otherwise it loops doing right shifts until the carry flag is set, counting how many loops that takes.That's between 8 and 29 instructions (not counting the return), depending on the value.In contrast my function with a table always uses exactly 4 instructions.Code:756: e5 b1 in r30, 0x05 ; 5758: f0 e0 ldi r31, 0x00 ; 075a: e8 59 subi r30, 0x98 ; 15275c: ff 4f sbci r31, 0xFF ; 25575e: c0 80 ld r12, Z(the first instruction there is reading PORTB, so I'm not counting it)All 8 bit table lookups will use exactly the same code (with different table addresses in the 2nd and 3rd instructions), no matter what the function being calculated is: find first bit set, find last bit set, find only bit set, calculate parity, count set bits ... whatever.

author: HwAoRrDk
author_rank: Super Contributor
timestamp: October 12, 2020, 08:39:36 am
content: Quote from: brucehoult on October 12, 2020, 08:20:18 amI didn't say it would be best, just convenient.Quote from: brucehoult on October 12, 2020, 08:20:18 amHuh? That's not what the docs say__builtin_ffsis supposed to do. It's supposed to return zero if the argument is zero.

author: brucehoult
author_rank: Super Contributor
timestamp: October 12, 2020, 08:57:42 am
content: Quote from: HwAoRrDk on October 12, 2020, 08:39:36 amAhh ... I misread the assembly language. The loading of a literal 8 is for another purpose.This is a count in a 16 bit variable. If the lo byte is 0 then it copies (actually ors) the hi byte into the lo byte and preloads the result with 8 before it starts looking for set bits in the hi byte. If both lo and hi bytes are 0 then it falls through to the first ret and returns 0 -- to be precise hi || (hi | lo) where || means bit concatenation and hi and lo were both 0.

author: mikerj
author_rank: Super Contributor
timestamp: October 12, 2020, 09:59:18 am
content: Quote from: brucehoult on October 12, 2020, 01:44:49 amNot particularly unusual, it's something I've had to do quite a number of times, the ARM Cortex has a count leading zeros instruction which makes this very fast.In a general case only requires a binary search with three compares to find the highest priority bit set, and the search can be programmed make either the highest or lowest bit the highest priority. Alternatively a 4 bit lookup table run on both nibbles would be fast and minimise program memory use.

author: Mechatrommer
author_rank: Super Contributor
timestamp: October 12, 2020, 02:19:31 pm
content: Quote from: Moriambar on October 11, 2020, 08:14:43 pmsometime cute code doesnt necessarily the fastest to run. your OP "ifs" example could run much faster..Quote from: mikerj on October 12, 2020, 09:59:18 amright, so here's one example... not the cutest, but should optimized for speed.. can be made generic function such as byte_to_index and hidden away somewhere else... (disclaimer: codes need further verification)Code:typedef uint8_t byte;byte byte_to_index(byte value) {if (value & 0b00001111) {if (value & 0b00000011) {if (value & 0b00000001) return 0;else return 1;} else { // assume (value & 0b00001100) trueif (value & 0b00000100) return 2;else return 3;};} else { // assume (value & 0b11110000) trueif (value & 0b00110000) {if (value & 0b00010000) return 4;else return 5;} else { // assume (value & 0b11000000) trueif (value & 0b01000000) return 6;else return 7;};};};more cuter code but involves more additions..Code:typedef uint8_t byte;byte byte_to_index(byte value) {byte result = 0;if (value & 0b11110000) result += 4;if (value & 0b11001100) result += 2;if (value & 0b10101010) result += 1;return result;};

author: NivagSwerdna
author_rank: Super Contributor
timestamp: October 12, 2020, 03:06:13 pm
content: Quote from: Moriambar on October 11, 2020, 07:17:31 pmActually a series of ifs is a really good way to do this on a atmega328. The reason being that this uP has instructions...BRBC – Branch if Bit in SREG is ClearedBRBS – Branch if Bit in SREG is Sethttp://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdfSoif (register & SINGLE_BIT_MASK) { } else... should be very efficient*...*Unless you have abstracted yourself from the hardware with Arduino Wiring etc.

author: Ian.M
author_rank: Super Contributor
timestamp: October 12, 2020, 03:57:35 pm
content: On any MCU with the ability to directly set/reset a single (fixed position) bit in a register in a single instruction, this variation of Mechatrommer's 'more cuter' code using a bitwise OR rather than addition may be preferable:Code:typedef uint8_t byte;byte byte_to_index(byte value) {byte result = 0;if (value & 0b11110000) result |= 4;if (value & 0b11001100) result |= 2;if (value & 0b10101010) result |= 1;return result;};

author: brucehoult
author_rank: Super Contributor
timestamp: October 13, 2020, 12:14:40 am
content: Quote from: Ian.M on October 12, 2020, 03:57:35 pmEither way, if it turns out that more than one bit is set then this can return the index of a bit that is *not* set, and without alerting you to this in any way, unlike my lookup table code.For example, if bits 3 and 5 are set then this will return that bit 7 is set.Your code (and Mechatrommer's) also don't distinguish between bit zero being set and nothing being set at all.

author: Ian.M
author_rank: Super Contributor
timestamp: October 13, 2020, 01:37:35 am
content: Its a tradeoff. Error checking costs code space and instruction cycles!Also the O.P. didn't specify what was to happen if a single bit wasn't set.Code:if (value&(value-1) || !value)can be used to detect the error cases of multiple or no bits set.

author: Mechatrommer
author_rank: Super Contributor
timestamp: October 13, 2020, 03:01:20 am
content: Quote from: brucehoult on October 13, 2020, 12:14:40 amif the mcu has extra 256 bytes of memory to waste, your 4 ops code is superb. there are 2 schools that i know in algorithm, one is to maximize speed regardless of memory or program space (thats your way), another one is compromize speed to get smallest memory or/and program footprint (thats ataradov's plain loop or probably my "more cuter" code unwrapped loop or/+ binary scan). sometime we need a bit of both to suit programming requirement and condition. we are not entirely sure whats the OP situation is... imho, i dont think N = 3 or even 7 or 8 is speed critical, i never thought to do binary scan method on such small operation, but then we dont know whats the OP is up to or if he can handle index = -1.

author: brucehoult
author_rank: Super Contributor
timestamp: October 13, 2020, 03:59:06 am
content: Quote from: Mechatrommer on October 13, 2020, 03:01:20 amNo, that's not "my way". I choose an appropriate trade-off depending on the circumstances. The thing is we don't know what the circumstances are.At least with the kind of things I do with AVRs, I tend to find the RAM pretty tight but I've never ever run out of flash memory for the program. This has been true for me across ATTiny85 (on which my lookup table will be 3% of the flash), ATMega328 (table will be 0.8% of the flash), and ATMega2560 (the table will be 0.1% of the flash). I pick them depending on the number of I/O pins (usually) or RAM (quite often) I need but ... so far ... I've never needed to move up to a bigger chip because my program code won't fit.Your mileage may vary.

author: Mechatrommer
author_rank: Super Contributor
timestamp: October 13, 2020, 04:45:04 am
content: Quote from: brucehoult on October 13, 2020, 03:59:06 amhave you programmed ATTiny13? my latest code is trying to play sine (and also square/pulse) PWM to motor realtime while listening to radio on interrupt and converting/reading/averaging adc values, always ended up cutting the program into half or make them separated if i want to embed calibration code in ittook hours at fondling code to the last bit, even sometime "+" or "|" does matter. talking about the wrong mcu maybe, i'm ordering ATTiny85 soon.

author: Nominal Animal
author_rank: Super Contributor
timestamp: October 13, 2020, 05:01:19 am
content: The below assume AVR-GCC with -mmcu=atmega328p -O2 or -Os, and that AVR-LIBC headers (as opposed to Arduino environment) is used for bare-metal programming. I've listed my suggestions with most preferred one first. I'm assuming taken jumps take 2 cycles, non-taken jumps one cycle. Flash access (LPM) on 328p takes 3 cycles.First one is basically what brucehoult suggested. The last one is what ataradov suggested. I use a four-bit shift since AVRs have nibble swap instruction in hardware.If you have enough Flash available, use a table lookup:Code:#include <avr/io.h>#include <avr/pgmspace.h>const unsigned char highest_bit_table PROGMEM = {8,0,1,8,2,8,8,8,3,8,8,8,8,8,8,8,4,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,5,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8};unsigned char highest_bit_set(unsigned char c){return pgm_read_byte_near(highest_bit_table + c);}which takes 262 bytes of Flash, and 7 cycles per access (assuming I counted right, no guarantees).If you need to conserve flash, useCode:#include <avr/io.h>#include <avr/pgmspace.h>const unsigned char highest_bit_table PROGMEM = {8,0,1,8,2,8,8,8,3,8,8,8,8,8,8,8,4,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,5,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,6,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,};unsigned char highest_bit_set(unsigned char c){if (c & 128)return 7;elsereturn pgm_read_byte_near(highest_bit_table + c);}which takes 148 bytes of flash, and 5 (if highest bit is set) or 9 cycles (otherwise).Note that if the inputs are uniformly random, the average is the same (7 cycles).To trade speed for less flash use, useCode:#include <avr/io.h>#include <avr/pgmspace.h>const unsigned char highest_bit_table_3 PROGMEM = {0x88, 0x40, 0x51, 0x88, 0x62, 0x88, 0x88, 0x88, 0x73, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,};unsigned char highest_bit_set_3(unsigned char c){if (c & 0xF0) {if (c & 0x0F)return 8;elsereturn pgm_read_byte_near(highest_bit_table_3 + (c >> 4)) >> 4;} elsereturn pgm_read_byte_near(highest_bit_table_3 + c) & 0x0F;}which takes 65 bytes of flash, and between 8 and 18 cycles.If you don't have even that much flash available, use the loop approach, but with a nibble swap since AVR's support that in hardware:Code:unsigned char highest_bit_set(unsigned char c){unsigned char result;if (c & 0xF0) {c >>= 4;result = 4;} else {result = 0;}while (c) {c >>= 1;result++;}return result;}This takes 32 bytes of flash, and 10 to 25 cycles per call, depending on the value.The pure loop approach,Code:unsigned char highest_bit_set(unsigned char c){unsigned char result = 8;while (c) {c >>= 1;++result;}return result;}takes 18 bytes of flash, and between 5 and 43 cycles.

author: brucehoult
author_rank: Super Contributor
timestamp: October 13, 2020, 05:07:08 am
content: Quote from: Mechatrommer on October 13, 2020, 04:45:04 amI have not.I just looked at digikey and ATTINY13-20SU is $0.94 and ATTINY85-20SU is $1.13, both qty 1 in 2.7-5.5V 20 MHz 1.27mm pin spacing 8-SOIC. I guess the difference is similar across other options for them.I guess I care about saving $0.19 a lot less than I care about 512b RAM vs 64b, 8k flash vs 1k etc. If you're making something in huge volumes then of course your trade-offs may be different. I'm just doing 1-off things.

author: Mechatrommer
author_rank: Super Contributor
timestamp: October 13, 2020, 05:14:45 am
content: well things happened. i just happened to have lot of them since many years. its an experience, now its time to upgrade. i guess at this age, its why some youngsters never know about what binary search or bit manipulation is, or even working with unsigned data type. those tiny13 will never go to waste for small projects cheers

author: RenThraysk
author_rank: Regular Contributor
timestamp: October 13, 2020, 11:03:54 am
content: Quote from: Nominal Animal on October 13, 2020, 05:01:19 amPretty sure the first conditions on these 3 solutions can be changed to comparisons (c > 0x0F), which is non destructive (doesn't require a temporary register to compute) so would save a move instruction.And a 16 byte lookup table on the last solution would be neat.

author: RenThraysk
author_rank: Regular Contributor
timestamp: October 13, 2020, 12:50:12 pm
content: Something like this.Code:#include <avr/io.h>#include <avr/pgmspace.h>const unsigned char t PROGMEM = {0,1,2, 2,3, 3, 3, 3,4, 4, 4, 4, 4, 4, 4, 4,};/* returns 0 when no bits set, 1 for least significant bit set, 8 when most */unsigned char highest_bit_set(unsigned char x) {unsigned char y = x / 16;if (y) {x = y;y = 4;}return y + pgm_read_byte_near(t + x);};

author: Mechatrommer
author_rank: Super Contributor
timestamp: October 13, 2020, 03:10:26 pm
content: Quote from: RenThraysk on October 13, 2020, 12:50:12 pmthis is what i was talking about "a bit of both" saving us time from some exercise.. but just looking at your table i can see some problem and cant handle unexpected errors as brucehoult highlighted, for example if x = 0 (no bit set) or x = 3 (more than one bits are set) your code still return valid index value. since you are using lookup table and includes invalid bits value, why dont you use as brucehoult's table some using -1, or just put value greater than 7 (unsigned operations) to flag error? maybe something like this? (unverified, just eyeballing) please also note the OP is using 0-based indexing. and i prefer x >> 4 with comment x / 16 because i dont trust the compiler is clever enough to switch division into bit shift, or through its infinite wisdom, do a more clever rounding off (iirc i've seen bigger machine did that) but your table is good for fast index rounding off when the program expects the behaviour. ymmvCode:#include <avr/io.h>#include <avr/pgmspace.h>const unsigned char t PROGMEM = {8,0,1, 8,2, 8, 8, 8,3, 8, 8, 8, 8, 8, 8, 8};/* returns 8 for invalid bits value, 0 for least significant bit set, 7 when most */unsigned char highest_bit_set(unsigned char x) {unsigned char y = x >> 4; // (x / 16)if (y) {x = y;y = 4;}return y + pgm_read_byte_near(t + x);};

author: RenThraysk
author_rank: Regular Contributor
timestamp: October 13, 2020, 03:30:26 pm
content: Quote from: Mechatrommer on October 13, 2020, 03:10:26 pmAVR GCC 5.4.0 does something strange with x >> 4 hence why used x / 16.unsigned char y = x >> 4; // (x / 16)Code:mov r30,r24ldi r31,0movw r18,r30asr r19ror r18asr r19ror r18asr r19ror r18asr r19ror r18mov r24,r18tst r18breq .L2unsigned char y = x / 16;Code:mov r25,r24swap r25andi r25,lo8(15)breq .L2Code here:https://godbolt.org/z/Ghffaj

author: Mechatrommer
author_rank: Super Contributor
timestamp: October 13, 2020, 03:43:28 pm
content: Quote from: RenThraysk on October 13, 2020, 03:30:26 pmyup apparantly avr dont support single op multiple bit shift, and the compiler is not clever enough to translate it into more efficient divide by 16 integer logicbut this is machine specific i usually only becomes aware when tuning space and time constraint. there is not entirely wrong or entirely right answer, cheers.

author: Nominal Animal
author_rank: Super Contributor
timestamp: October 13, 2020, 03:56:24 pm
content: Like Mechatrommer pointed out, there are many ways to define the desired behaviour. For example:Using avr-gcc 5.4.0 (it's what I have installed right now),Code:#include <avr/pgmspace.h>#define UNLIKELY(expr) __builtin_expect(expr, 0)#define LIKELY(expr) __builtin_expect(expr, 1)/* ((BAD | 4) == BAD) must be true. */#define BAD 15static const unsigned char one_bit_table PROGMEM = {BAD, 0, 1, BAD, 2, BAD, BAD, BAD, 3, BAD, BAD, BAD, BAD, BAD, BAD, BAD,};unsigned char one_bit_set(unsigned char value){unsigned char result = 0;if (value > 15) {value = __builtin_avr_swap(value);if (UNLIKELY(value > 15))return BAD;elseresult = 4;}return result | pgm_read_byte_near(one_bit_table + value);}which needs 36+16 = 52 bytes of flash, and takes 7 cycles if bad (argument is zero or has more than one bit set), and 13 or 17 cycles otherwise.(You can choose any value forBAD, as long as((BAD|4)==BAD). On AVRs, this includes 15 and 255. Note that as usual, on AVR, casting 255 tosigned char, yields -1.)The following implementation finds the highest bit set in a byte:Code:#include <avr/io.h>#include <avr/pgmspace.h>#define LIKELY(expr) __builtin_expect(expr, 1)#define NONE 8const unsigned char high_bit_table PROGMEM = {NONE, 0, 1,1, 2,2,2,2, 3,3,3,3,3,3,3,3,};unsigned char highest_bit_set(unsigned char c){if (LIKELY(c > 15))return pgm_read_byte_near(high_bit_table + (c >> 4)) + 4;if (LIKELY(c > 0))return pgm_read_byte_near(high_bit_table + c);return NONE;}using 44+16 = 60 bytes, and runs in 7 (zero) or 12/13 cycles. You can freely choose the value ofNONE; and the first byte in the array (set to NONE) is never accessed, so you can reuse it for something else.The difference here is that if you have multiple bits set, this one returns the index of the highest bit set instead of an error.The two functions assembly code isCode:.textone_bit_set:cpi r24, lo8(16)brlo .lownibbleswap r24cpi r24, lo8(16)brsh .notonebitldi r25, lo8(4)rjmp .lookup.lownibble:ldi r25, 0.lookup:mov r30, r24ldi r31, 0subi r30, lo8(-(one_bit_table))sbci r31, hi8(-(one_bit_table))lpm r30, Zmov r24, r25or r24, r30ret.notonebit:ldi r24, lo8(15)rethighest_bit_set:cpi r24, lo8(16)brlo .lowhalfmov r30, r24swap r30andi r30, lo8(15)ldi r31, 0subi r30, lo8(-(high_bit_table))sbci r31, hi8(-(high_bit_table))lpm r30, Zldi r24, lo8(4)add r24, r30ret.lowhalf:tst r24breq .zeromov r30, r24ldi r31, 0subi r30, lo8(-(high_bit_table))sbci r31, hi8(-(high_bit_table))lpm r24, Zret.zero:ldi r24, lo8(8)rethigh_bit_table:.byte 8.byte 0.byte 1.byte 1.byte 2.byte 2.byte 2.byte 2.byte 3.byte 3.byte 3.byte 3.byte 3.byte 3.byte 3.byte 3one_bit_table:.byte 15.byte 0.byte 1.byte 15.byte 2.byte 15.byte 15.byte 15.byte 3.byte 15.byte 15.byte 15.byte 15.byte 15.byte 15.byte 15

author: Mechatrommer
author_rank: Super Contributor
timestamp: October 13, 2020, 03:56:55 pm
content: maybe you can try what it generates if x >> 3 vs x / 8 and x >> 6 vs x / 64.. when there is no high and low nibble swap, sometime compiler does wonderfull things...

author: Nominal Animal
author_rank: Super Contributor
timestamp: October 13, 2020, 05:08:43 pm
content: When optimizations are enabled (-O1, -O2, -O3, -Os), avr-gcc 5.4.0 generates logical shifts of unsigned chars (Rn) at most four instructions long:Rn << 1: LSL RnRn << 2: LSL Rn ; LSL RnRn << 3: LSL Rn ; LSL Rn ; LSL RnRn << 4: SWAP Rn ; ANDI Rn, -16Rn << 5: SWAP Rn ; LSL Rn ; ANDI Rn, -32Rn << 6: SWAP Rn ; LSL Rn ; LSL Rn ; ANDI Rn, -64Rn << 7: ROR Rn ; CLR Rn ; ROR RnRn >> 1: LSR RnRn >> 2: LSR Rn ; LSR RnRn >> 3: LSR Rn ; LSR Rn ; LSR RnRn >> 4: SWAP Rn ; ANDI Rn, 15Rn >> 5: SWAP Rn ; LSR Rn ; ANDI Rn, 7Rn >> 6: SWAP Rn ; LSR Rn ; LSR Rn ; ANDI Rn, 3Rn >> 7: ROL Rn ; CLR Rn ; ROL RnSimilarly, both-1-Rnand-(Rn+1)are compiled to a single instructionCOM Rn.(Each of the instructions used above takes only one cycle.)While AVR does have a two-cycle signed and unsigned multiplication, the 16-bit result is always in register pair R1:R0. For AVR-GCC, R1 will need to be cleared to zero. So, technically, one could implement e.g.Rn << 6: LDI R1, 64 ; MUL Rn, R1 ; MOV Rn, R1 ; CLR R1but it would be just as long, and take an extra cycle to execute.

author: RenThraysk
author_rank: Regular Contributor
timestamp: October 13, 2020, 05:21:49 pm
content: Seems the AVR GCC 5.4.0 compiler version on godbolt is generating the strange code for >> 4.Local compiler is correctly recognising the optimisation of the shift to swap & and.Other versions 4.6.4 & 4.5.4 on godbolt also recognise the optimisation.Bizzare.Edit: Further investigationavr-g++ 5.4.0 generates the strange code for y = x >> 4, and is what godbolt uses when says it's using AVR gcc 5.4.0.avr-gcc 5.4.0 generates expected optimised code.

author: RenThraysk
author_rank: Regular Contributor
timestamp: October 13, 2020, 05:50:29 pm
content: Quote from: Mechatrommer on October 13, 2020, 03:10:26 pmUsing 0 is just what used to, main language is Go these days and it's intrinsic function bits.Len(x) returns minimum number of bits required to represent x. If x = 0 then it returns 0.

author: radiolistener
author_rank: Super Contributor
timestamp: October 14, 2020, 10:11:09 am
content: I have similar question, I need solution for verilog, but I will be glad for solutions in any language.The problem is the following, there is about 1024 bit register which consists waveform fingerprint, and I need to convert it into binary representation for period of 1. Actually this is output of TDC (time to digital converter) ADC implemented in FPGA. And I want to convert it into usable format.For example:input: 0000000010000000000001000000000...output: 1input: 0000000011000000000001100000000...output: 2input: 0000000011110001111000000000000...output: 4etc...The problem here is that register is very long and I need to transform it at high speed rate (about 100-200 MHz), so there is no way to serialize it with counting, because it will take at least 1024 clock cycles. There is also no way to predict where 1 starts.Any idea?

author: Mechatrommer
author_rank: Super Contributor
timestamp: October 14, 2020, 10:30:35 am
content: with 128 bytes binary search for nonzero, thats log2(128)=7 steps, and then some housekeeping job to see how much adjacent 1's.. no experience in verilog programming though, not sure how to access array's elements.

author: brucehoult
author_rank: Super Contributor
timestamp: October 14, 2020, 12:06:32 pm
content: Quote from: Mechatrommer on October 14, 2020, 10:30:35 amYou can't binary search for nonzero because if you don't find 1s at the midpoint you have no way to know whether you shoudl search the first half or the second half.

author: Mechatrommer
author_rank: Super Contributor
timestamp: October 14, 2020, 12:33:16 pm
content: right with no particular ordering or clue, you could end up worst case 128 steps

author: brucehoult
author_rank: Super Contributor
timestamp: October 14, 2020, 12:54:02 pm
content: Quote from: radiolistener on October 14, 2020, 10:11:09 amWhat do you want to do if there are multiple runs of 1s in the input but they have different lengths? Average them? Also, what if the register starts with 1s and/or ends with 1s, in which case you may have only a partial run there. For maximum accuracy you should ignore them, but then you have to hope there are other runs in the middle.Anyway the problem reduces to counting how many 1s there are in total (ignoring any at the very start or very end), and how many transitions from 0 to 1 (or from 1 to 0, which will be the same if you ignore runs at the ends)You can do this very simply with a state machine linked to three counters:Initialize runs=0, bits=0, partial=0, state=0state 0: 0 -> state=1; 1 -> state=0; // skip initial 1sstate 1: 0 -> state=1; 1 -> ++runs, ++bits, ++partial, state=2; // skip a run of 0sstate 2: 0 -> partial=0, state=1; 1 -> ++bits, ++partial, state=2 // count run lengthEnd of input encountered (in any state):if (partial != 0){bits -= partial; --runs;}output = bits/runs;Translating that directly to verilog implemented in an FPGA shouldn't have any problems keeping up.If you want to run it in software then you can process (for example) a byte at a time by having several 256 element lookup tables for each state (at the start of that input byte), telling which state is next (0..2), how much to add to runs (0..4), how much to add to bits (0.., and how much to add to or copy to partial (1..8 or 0..7).If you're on a 32 bit CPU you could encode all that into a single 32 bit value and update everything in parallel with branchless code at I think just under one instruction per input bit.

author: radiolistener
author_rank: Super Contributor
timestamp: October 14, 2020, 05:25:06 pm
content: Quote from: brucehoult on October 14, 2020, 12:54:02 pmI never worked with processing of rawTDCresults, so I don't know what exactly I need to count, this is my first experience with TDC. At a glance there is need to calculate signal period or positive and negative pulse period. But I'm not sure, because I never worked with such problem before. So, it needs to perform experiments.Quote from: brucehoult on October 14, 2020, 12:54:02 pmThe problem here is that I need to get result after max 2-4 steps.Quote from: brucehoult on October 14, 2020, 12:54:02 pmThis is not CPU. This is logic hardware circuit. So, technically this is 1024 bit register. I cannot split this operation into many cycles, because there is limit for the frequency. I need to do it within 2-4 steps, but no more. On the other hand I can do many logic operations simultaneously. I can use many LUT tables to implement decoders, but there is limited memory (let's say 128 KB for all things).Is it possible to count length of 1 series with logic and small LUT tables?For 8 bit it can be easily done with 256 byte LUT.But I need to do it for 1024 bit, so it requires 2^1024= 1.79^308 memory cells

author: Nominal Animal
author_rank: Super Contributor
timestamp: October 14, 2020, 11:18:26 pm
content: Edited: The two previous versions were incorrect!I would use a Game-Of-Life approach for this.Essentially, you have a 1024×1024 cell/bit matrix, where the sample data is at the very top, and changes every sample cycle. Each bit is handled by a cell that has three inputs, and two outputs:┼─┼─┼─┼ A 0 0 0 0 1 1 1 1│A│B│C│ B 0 0 1 1 0 0 1 1┼─┼─┼─┼ C 0 1 0 1 0 1 0 1│X│ X 0 0 0 0 0 0 1 1 X = B & A┼─┼ Y 0 0 1 0 0 0 0 0 Y = B & !A & !CThe X output will be an input to three cells on the next level (down left, down, and down right), and the Y output will go to a summer on each row (producing the number of set Y's on that row). The number of set Y's on row R means that the sample R cycles before had sum(Y) sequences of length R.Essentially, when the data percolates down, each run of bits set shrinks from the left, and when they vanish, the Y output gets set on the row matching their length.Therefore, on the top row of cells, the number of Y set is the number of 1-bit sequences in the data; on the second row of cells, the number of 2-bit sequences, and so on.Note that on top row (of processing cells), at most one of each consecutive pair of Y's can be set at a time, so pairs can be OR'd together. There are 512 of Y inputs to count on that row, and the count is between 0 and 256, inclusive.On the second row, at most one of each set of three Y's can be set at a time, so triplets can be OR'd together. The count is between 0 and 171, inclusive.On the seventh row, each consecutive set of eight Y's are OR'd together. The count is between 0 and 32, inclusive.Also note that any contiguous sequence of zeros will grow by one when percolating down. This means some sort of compression for long runs, making the cell grid a triangle rather than a square, and reducing the total number of cells, is possible.The total latency is 1024 cycles (or more), but each cycle you get new results. There is nodelay, because each stage works on data in parallel, just data from a different input sample set!To count the Y signals on each row, a triangular cell map could be used; the top row would be the Y inputs on each sample cycle, and the actual count pops out at the end.Yes, it adds up very, very fast. I'm not sure if this is feasible given current FPGA sizes – I'm just a hobbyist with zero experience with FPGAs, but lots of experience in programming (distributed simulations) –, but this is what I'd try.Because the 1024×1024 bit map is simply a rectangular region of trivial logic gates, it might need an ASIC. The Y signal summers are just counting the number of set inputs for each sample cycle, so they will need multiple stages and more complex circuitry, but each row works in parallel, and the more there is to do, the more stages you can use before the result is needed (assuming the counts of set bit sequence lengths are provided at the same time for each input, although a staggered mode is easy to un-stagger in software). So, I'm absolutely certain this is doable, even if the circuitry only runs at twice or thrice the sample frequency.

author: brucehoult
author_rank: Super Contributor
timestamp: October 14, 2020, 11:42:51 pm
content: It's a bit difficult if you don't know what you are actually supposed to be calculating :-)If you need it to be fast and you don't care how much hardware it takes then you basically just do a binary tree structured reduction of what I showed a linear reduction of.

author: brucehoult
author_rank: Super Contributor
timestamp: October 14, 2020, 11:51:34 pm
content: Quote from: Nominal Animal on October 14, 2020, 11:18:26 pmI agree with your general idea, but don't quite follow your detailed logic.In particular, the last value in your X row doesn't agree with the formula -- when A, B, C are all 1 then C & B & !A is 0. Only 011 can produce a 1 for X.

author: Nominal Animal
author_rank: Super Contributor
timestamp: October 15, 2020, 08:52:11 am
content: Okay, I had a bit of problems explaining my logic above, but it is now fixed.I know, because I wrote a simulator in C, that takes a single input on the command line (only ones and zeros in arguments are considered, others are ignored), and simulates one time step of each (identical) row in the matrix. It assumes there are implicit leading and trailing zeros in the input.Code:#include <stdlib.h>#include <stdint.h>#include <string.h>#include <stdio.h>/* Each input word contains 62 bits, aligned to the least significant bit. Two highest bits are unused. */typedef uint64_t u62;static inline unsigned int bits_set(const u62 u){#if defined(__GNUC__)if (sizeof u >= sizeof (unsigned long long))return __builtin_popcountll(u);if (sizeof u >= sizeof (unsigned long))return __builtin_popcountl(u);if (sizeof u >= sizeof (unsigned int))return __builtin_popcount(u);#endifuint64_t m = u;m = (m & UINT64_C(0x5555555555555555)) + ((m >> 1) & UINT64_C(0x5555555555555555));m = (m & UINT64_C(0x3333333333333333)) + ((m >> 2) & UINT64_C(0x3333333333333333));m = (m & UINT64_C(0x0F0F0F0F0F0F0F0F)) + ((m >> 4) & UINT64_C(0x0F0F0F0F0F0F0F0F));m = (m & UINT64_C(0x00FF00FF00FF00FF)) + ((m >> 8) & UINT64_C(0x00FF00FF00FF00FF)); /* 0x001F001F001F001F */m = (m & UINT64_C(0x0000FFFF0000FFFF)) + ((m >> 16) & UINT64_C(0x0000FFFF0000FFFF)); /* 0x0000003F0000003F */m = (m & UINT64_C(0x00000000FFFFFFFF)) + ((m >> 32) & UINT64_C(0x00000000FFFFFFFF)); /* 0x000000000000007F */return m;}static unsigned int process_row(u62 state[], const size_t words){size_t i;unsigned int n = 0;/* Clear the extra bits. */for (i = 0; i < words; ++i)state &= UINT64_C(0x7FFFFFFFFFFFFFFE);/* Copy the LSB down, and MSB up. */for (i = 1; i < words; ++i) {state[i-1] |= (state & 2) << 62;state |= (state[i-1] >> 62) & 1;}/* Process each word. */for (i = 0; i < words; ++i) {const u62 s = state;/* Calculate the outputs, shifting the word back down.Ensure the extra bits will be zero. */state = ((s >> 1) & UINT64_C(0x7FFFFFFFFFFFFFFF) & s);/* Calculate the number of dying sequences, and add to the sum. */n += bits_set(s & (~(s >> 1)) & (~(s << 1)) & UINT64_C(0x7FFFFFFFFFFFFFFE));}return n;}int main(int argc, char *argv[]){u62 *word;size_t w, words;size_t bit, bits;int arg;if (argc < 2 || !strcmp(argv, "-h") || !strcmp(argv, "--help") || !strcmp(argv, "/?")) {const char *argv0 = (argc > 0 && argv && argv) ? argv : "(this)";fprintf(stderr, "\n");fprintf(stderr, "Usage: %s [ -h | --help | /? ]\n", argv0);fprintf(stderr, " %s BIT STRING ...\n", argv0);fprintf(stderr, "\n");fprintf(stderr, "This program returns the counts of consecutive runs of ones, in order of length.\n");fprintf(stderr, "\n");fprintf(stderr, "All characters except 0 and 1 in input parameters are ignored.\n");fprintf(stderr, "\n");return EXIT_SUCCESS;}/* First, count the number of bits in input. */for (bits = 0, arg = 1; arg < argc; arg++) {const char *a = argv;while (*a) {if (*a == '0' || *a == '1')bits++;a++;}}if (bits < 1) {fprintf(stderr, "Invalid input!\n");return EXIT_FAILURE;}/* Number of words needed. Round UP, and add an extra word for paranoia. */words = (bits + 61) / 62;word = calloc(words + 1, sizeof (u62));if (!word) {fprintf(stderr, "Not enough memory for %zu bits (%zu 62-bit words).\n", bits, words);return EXIT_FAILURE;}/* We store the bits in the middle bits of each word, 1..62, inclusive. */for (w = 0, bit = 0, arg = 1; arg < argc; arg++) {const char *a = argv;while (*a) {if (*a == '0' || *a == '1') {/* Advance to next word? */if (bit >= 62) {w++;bit = 0;}/* Set bit. */word |= ((uint64_t)(*a == '1')) << (++bit);}a++;}}/* Paranoid safety check. */if (bits != 62*w + bit || w >= words) {fprintf(stderr, "Internal bug parsing input. Counted %zu bits, but parsed %zu.\n", bits, 62*w+bit);return EXIT_FAILURE;}/* We simulate each row exactly once. Because of this, we only need one row buffer. */for (size_t row = 1; row <= bits; row++) {const unsigned int n = process_row(word, words);if (n == 1)printf("%9u sequence of length %zu\n", n, row);elseif (n > 0)printf("%9u sequences of length %zu\n", n, row);}return EXIT_SUCCESS;}For example, if you compile and run the above using e.g../above 0101 11 111010111, the output is2 sequences of length 11 sequence of length 31 sequence of length 6Because the simulator simulates each row for only one step, you get the counts in order of sequence length.Since each row is dependent on the output of the row above, all rows can be implemented in parallel. And, as the above code shows, they can be identical – the sameprocess_row()function is called for each row; it modifies the state in-place.Essentially, this implementation uses 64-bit units, for each input of 62 unique bits (the two extra bits are the extra A and C inputs from the preceding and following units), producing 62-bit result, and a single count (number of Y bits set on the line).As you can see, the logic for outputXwith inputsB(above),A(above left), andC(above right) isX=B&Aand for outputY(sequence "died"),Y=B& !A& !CThis means there are a huge number of options on how to implement this. Indeed, the 1024×1024 matrix itself is just 1,048,576 AND gates (running at the input sample rate, if the output of each gate is latched, storing one bit of the matrix). (That does not include the logic needed to count the number of Y set on each row, though.)

author: brucehoult
author_rank: Super Contributor
timestamp: October 15, 2020, 11:03:35 am
content: I applaud the effort of reducing the concept to practice and proving it works. Well done that dude.