Reputation: 51
The problem I face is that the Attiny167 and Attiny87 despite on the datasheets saying they are drop in replacements are in fact not. The program is written in AVR assembly.
The vector table for the Attiny87 uses RJMP meanwhile the Attiny167 uses JMP. This means I need two separate instances of the vector table in my program somehow which I don't believe is possible. Is there a way to write the code so that the program can check the signature bytes to check which hardware device is on and then use the appropriate "jmp" instruction? Thank you.
I have tried so far writing two separate instances of the program using whichever required jmp instruction, however I need a way for the program to do this automatically instead of having to manually check.
Upvotes: 5
Views: 137
Reputation: 4003
ATtiny167 and ATtiny87 despite on the datasheets saying they are drop in replacements are in fact not.
"Drop-In Replacement" means that it is possible to replace one controller by the other one physically, which includes:
It does not imply that the chips are binary compatible, i.e. that they can be programmed using the same binary code / executable.
Edit1:
In the ATtiny87/167 Datasheet "7.1 Interrupt Vectors in ATtiny87/167", pp 57 the interrupt addresses of either device are different.
In "1.1 Comparison between ATtiny87 and ATtiny167" the vector size of 2 words for ATtiny87 is a Bug in the manual.
ATtiny87 and ATtiny167 are not binary compatible. To get a binary that's compatible requires some effort... Compile your code for ATtiny87 but with the following modifications:
Write a startup code (crtattiny87.o
) for ATtiny87 that has twice as many IRQ vectors like the actual hardware. Then, if you need an ISR for IRQ N, also implement IRQ 2N. If an IRQ triggers on ATtiny87, you will get IRQ N; if it triggers on ATtiny167, you will get IRQ 2N.
Then determine at runtime, which IRQ actually triggered and forward to the appropriate ISR code. (No idea how to discriminate between the two µC's at runtime, though.) If you only need few IRQs, the IRQ source might be clear without a runtime check.
RJMP
and RCALL
instructions behave differently: They wrap around at 8KiB on ATtiny87, but wrap around at 16KiB on ATtiny167. So you must make sure that the binary does not use wrap-around in these instructions. You can determine wrap-around in a disassembly when the absolut byte address is outside [0,0x1ffe], e.g. with avr-objdump -d x.elf
for example:1860: f4 d3 rcall .+2024 ; 0x204a <positive wrap-around>
46: 0e cc rjmp .-2020 ; 0xfffff864 <negative wrap-around>
Make sure the binary has no such wrap-arounds and if, re-organize the code accordingly.
Upvotes: 1
Reputation: 962
I am surprised no one proposed using assembler macros, suppose your interrupt vector routine is at address 0x1200
, and that your interrupt vector address is at at address 0x0006
:
.ORG 0x0006
.IF DEFINED(__ATtiny87__)
RJMP 0x1200
.ELIF DEFINED(__ATtiny167__)
JMP 0x1200
.ENDIF
You can alternatively do something a bit more dirty but a bit cooler using the preprocessor:
#define MJUMP defined(__ATtiny87__) ? RJMP : JMP ; PASTE THIS ANYWHERE IN YOUR CODE
For this second method, you can basically now replace every instance of JMP
and RJMP
with MJMP
everywhere in your code, and it will solve your problem more globally (not just with interrupts).
Upvotes: 0
Reputation: 58802
The simple solution is to have your reset
read the signature byte into a variable so you can branch on that later.
Initial code could look like:
rjmp reset
rjmp vec_1
rjmp vec_2
rjmp vec_3
...
vec_1:
; odd vectors are always attiny87
; this is INT0
; attiny167 never gets here so no branching
...
vec_2:
; if we are attiny87 this is INT1
; if we are attiny167 this is INT0
; so do a conditional branch to the proper place
...
vec_3:
; again odd vector so this is PCINT0 on an attiny87
...
If you don't like the repeated branching you can put together a clever dispatcher working with RCALL
and the return addresses. This may be overkill but was fun so here it is:
#include <avr/io.h>
; set up a variable to store the signature byte
; arbitrary address
.equ signature_byte_1, 0x100
rjmp reset
.rept 39
rcall dispatch
.endr
dispatch:
; save Y
push r28
push r29
; get SP into Y
in r28, _SFR_IO_ADDR(SPL)
in r29, _SFR_IO_ADDR(SPH)
; put ZH on stack
std Y + 3, r31
; get return address into ZH
ldd r31, Y + 4
; put ZL on stack
std Y + 4, r30
; save SREG
in r30, _SFR_IO_ADDR(SREG)
push r30
; assume this has been set up during reset
; 0x93 for attiny87
; 0x94 for attiny167
lds r30, signature_byte_1
; return address is
; attiny87: 2, 3, 4, ...
; attiny167: 3, 5, 7, ...
; we want for indexing: 0, 2, 4 ...
dec r31
; multiply attiny87 by 2
sbrc r30, 0
lsl r31
; now both are 2, 4, 6, ...
; fix pointer, assume it's in first 256 bytes
mov r30, r31
ldi r31, 0
ldi r28, lo8(vectors-2)
add r30, r28
; both LPM and ICALL use Z :(
lpm r28, Z+
lpm r29, Z
mov r30, r28
mov r31, r29
icall
; restore SREG
pop r30
out _SFR_IO_ADDR(SREG), r30
; restore Y
pop r29
pop r28
; restore Z
pop r31
pop r30
; done
reti
; here is the real vector table
vectors:
.word pm(int0_handler)
.word pm(int1_handler)
.word pm(pcint0_handler)
.word pm(pcint1_handler)
; ...
reset:
ldi r16, lo8(RAMEND)
ldi r17, hi8(RAMEND)
out _SFR_IO_ADDR(SPL), r16
out _SFR_IO_ADDR(SPH), r17
; read signature byte into variable
; left as exercise for reader :)
; TEST CODE
; verify these are not changed
ldi r28, 28
ldi r29, 29
ldi r30, 30
ldi r31, 31
; simulate attiny87
ldi r16, 0x93
sts signature_byte_1, r16
; invoke PCINT0
rcall 3*2
; invoke INT1
rcall 2*2
; simulate attiny167
ldi r16, 0x94
sts signature_byte_1, r16
; invoke PCINT0
rcall 6*2
; invoke INT1
rcall 4*2
end:
rjmp end
int0_handler:
ret
int1_handler:
ret
pcint0_handler:
ret
pcint1_handler:
ret
Upvotes: 3
Reputation: 819
According to the catalog sheet, both types are hw and sw compatible. Both use two words for the vector table. One word (rjmp) can be used only if the FLASH size is less than 4KiBy.
Atmel® ATtiny87 and ATtiny167 are hardware and software compatible. They differ only in memory sizes as shown in Table 1-1
So you can use JMP for both MCU
In the case of using several incompatible types, this is solved by conditional translation, for example according to signature or family type.
Edit:
I was confused by the information in table 1.1. Where it is written for ATTiny87 that it uses 2 words for the table of interrupt vectors. In fact, it only uses one word as rjmp can jumps to 8KiBy (+-2KWord). In that case, conditional translation can be used. For example in AVRASM2
like this
.if SIGNATURE_000==0x1e && SIGNATURE_001==0x93 && SIGNATURE_002==0x87 ;ATTiny87
rjmp RESET ; Reset Handler
rjmp INT0addr ; IRQ0 Handler
rjmp INT1addr ; IRQ1 Handler
rjmp PCINT0addr ; PCINT0 Handler
rjmp PCINT1addr ; PCINT1 Handler
rjmp WDTaddr ; Watchdog Timer Handler
rjmp ICP1addr ; Timer1 Capture Handler
rjmp OC1Aaddr ; Timer1 Compare A Handler
rjmp OC1Baddr ; Timer1 Compare B Handler
rjmp OVF1addr ; Timer1 Overflow Handler
rjmp OC0Aaddr ; Timer0 Compare A Handler
rjmp OVF0addr ; Timer0 Overflow Handler
rjmp LINTCaddr ; LIN Transfer Complete Handler
rjmp LINERRaddr ; LIN Error Handler
rjmp SPIaddr ; SPI Transfer Complete Handler
rjmp ADCCaddr ; ADC Conversion Complete Handler
rjmp ERDYaddr ; EEPROM Ready Handler
rjmp ACIaddr ; Analog Comparator Handler
rjmp USISTARTaddr ; USI Start Condition Handler
rjmp USIOVFaddr ; USI Overflow Handler
.endif
.if SIGNATURE_000==0x1e && SIGNATURE_001==0x94 && SIGNATURE_002==0x87 ;ATTiny167
jmp RESET ; Reset Handler
jmp INT0addr ; IRQ0 Handler
jmp INT1addr ; IRQ1 Handler
jmp PCINT0addr ; PCINT0 Handler
jmp PCINT1addr ; PCINT1 Handler
jmp WDTaddr ; Watchdog Timer Handler
jmp ICP1addr ; Timer1 Capture Handler
jmp OC1Aaddr ; Timer1 Compare A Handler
jmp OC1Baddr ; Timer1 Compare B Handler
jmp OVF1addr ; Timer1 Overflow Handler
jmp OC0Aaddr ; Timer0 Compare A Handler
jmp OVF0addr ; Timer0 Overflow Handler
jmp LINTCaddr ; LIN Transfer Complete Handler
jmp LINERRaddr ; LIN Error Handler
jmp SPIaddr ; SPI Transfer Complete Handler
jmp ADCCaddr ; ADC Conversion Complete Handler
jmp ERDYaddr ; EEPROM Ready Handler
jmp ACIaddr ; Analog Comparator Handler
jmp USISTARTaddr ; USI Start Condition Handler
jmp USIOVFaddr ; USI Overflow Handler
.endif
Upvotes: 0