I noticed this very smart coding trick Chip used in checking for a hex character (0..9, A..F, a..f).
Code and table must be in cog.
I also found a shorter way to convert it to a binary nibble.
hexchrs long %00000000_00000000_00000000_00000000
long %00000011_11111111_00000000_00000000 '"0".."9"
long %00000000_00000000_00000000_01111110 '"A".."F"
long %00000000_00000000_00000000_01111110 '"a".."f"
long %00000000_00000000_00000000_00000000
long %00000000_00000000_00000000_00000000
long %00000000_00000000_00000000_00000000
long %00000000_00000000_00000000_00000000
.check altb x,#hexchrs 'check for hex
testb 0,x wc
if_nc ret 'if not hex, c=0
testbn x,#6 wz 'hex, "0".."9"?
if_nz add x,#9 '..make low nibble $A..$F
_ret_ and x,#$F 'extract low nibble, return c=1
The add x,#9 converts "A..F" to "J..O" and "a..f" to "j..o". "J" is ascii $4A and "j" is ascii $6A.
Then we strip off the upper nibble from both the "0..9" and "J..O"/"j..o" leaving $00..$0F"
While it's shorter code to first check for ASCII $80 and above, rather than using the 8 long table we could use a 4 long table. This would save 2 longs.
However, Chip is looking for the fastest code.
This should work. Just fill in the values and the values will be calculated for you... (samples shown for P2-EVAL & P2D2 boards with 150MHz & 148.5MHz selected respectively)
CON
'+-------[ Select for P2-EVAL ]------------------------------------------------+
_XTALFREQ = 20_000_000 ' crystal frequency
_XDIV = 2 '\ '\ crystal divider to give 10.0MHz
_XMUL = 15 '| 150 MHz '| crystal / div * mul to give 150MHz
_XDIVP = 1 '/ '/ crystal / div * mul /divp to give 150MHz
_XOSC = %10 '15pF ' %00=OFF, %01=OSC, %10=15pF, %11=30pF
'+-------[ Select for P2D2 ]---------------------------------------------------+
_XTALFREQ = 12_000_000 ' crystal frequency
_XDIV = 4 '\ '\ crystal divider to give 3.0MHz
_XMUL = 99 '| 148.5MHz '| crystal / div * mul to give 297.0MHz
_XDIVP = 2 '/ '/ crystal / div * mul /divp to give 148.5MHz
_XOSC = %01 'OSC ' %00=OFF, %01=OSC, %10=15pF, %11=30pF
'+-----------------------------------------------------------------------------+
_XSEL = %11 'XI+PLL ' %00=rcfast(20+MHz), %01=rcslow(~20KHz), %10=XI(5ms), %11=XI+PLL(10ms)
_XPPPP = ((_XDIVP>>1) + 15) & $F ' 1->15, 2->0, 4->1, 6->2...30->14
_CLOCKFREQ = _XTALFREQ / _XDIV * _XMUL / _XDIVP ' internal clock frequency
_SETFREQ = 1<<24 + (_XDIV-1)<<18 + (_XMUL-1)<<8 + _XPPPP<<4 + _XOSC<<2 ' %0000_000e_dddddd_mmmmmmmmmm_pppp_cc_00 ' setup oscillator
_ENAFREQ = _SETFREQ + _XSEL ' %0000_000e_dddddd_mmmmmmmmmm_pppp_cc_ss ' enable oscillator
_1us = _clockfreq/1_000_000 ' 1us
DAT org 0
'+-------[ Set Xtal ]----------------------------------------------------------+
entry hubset #0 ' set 20MHz+ mode
hubset ##_SETFREQ ' setup oscillator
waitx ##20_000_000/100 ' ~10ms
hubset ##_ENAFREQ ' enable oscillator
'+-----------------------------------------------------------------------------+
Oops. Fixed this _SETFREQ = 1<<24 +.. was 1<<25 Updated: 22 Feb 2019
Cogs share the configuring of each per-pin mode register.
A pin will stay configured even if all cogs are restarted.
WRPIN {#}D,{#}S Write D to mode register of pin S[5:0], acknowledge smartpin if operating.
bit 30 25 20 15 10 5 0
| | | | | | |
D/# = %AAAA_BBBB_FFF_PPPPPPPPPPPPP_TT_MMMMM_0
2 1 1
0 5 0
%PPPPPPPPPPPPP: low-level custom pin control
common labelling of pin config bits
%C = clocked/registered I/O (extra clock for IN and OUT)
%I = invert IN output
%O = invert OUT input
%HHH/LLL = digital out drive strength
000: Fast, 20R
001: 1k5R
010: 15kR
011: 150kR
100: 1mA
101: 100uA
110: 10uA
111: Float
PinA is specified pin number
PinB is PinA's odd/even pair
0 5 0
%0_VVV_CIOHHHLLL = Digital mode, 3.3 volt
DIR enables PinA digital output
%VVV = Digital config
000: IN = PinA logic, PinA output from OUT
001: IN = PinA logic, PinA output from IN
010: IN = PinB logic, PinA output from IN
011: IN = PinA schmitt, PinA output from OUT
100: IN = PinA schmitt, PinA output from IN
101: IN = PinB schmitt, PinA output from IN
110: IN = PinA > PinB comparator, PinA output from OUT
111: IN = PinA > PinB comparator, PinA output from IN
0 5 0
%100_VVV_OHHHLLL = ADC_MODE, first order sigma-delta
IN has bitstream, sysclock bitrate, for smartpin mode %01111 (Y=0)
OUT is PinA digital output, registered
DIR enables PinA digital output
%VVV = ADC config
000: GIO, 1x (~5 volt range, centred on VIO/2)
001: VIO, 1x "
010: PinB, 1x "
011: PinA, 1x "
100: PinA, 3.16x (~1.58 volt range, centred on VIO/2)
101: PinA, 10x (~0.5 volt range, centred on VIO/2)
110: PinA, 31.6x (~0.158 volt range, centred on VIO/2)
111: PinA, 100x (~0.05 volt range, centred on VIO/2)
0 5 0
%101_VV_DDDDDDDD = DAC_MODE (%TT = 00 and %MMMMM = 00000), 8-bit flash, registered
OUT enables PinA ADC (ADC config %011), sysclocked bitstream on IN
DIR enables PinA DAC output
%VV = PinA DAC config
00: 990 ohm, 3.3 volt range
01: 600 ohm, 2.0 volt range
10: 123.75 ohm, 3.3 volt range
11: 75 ohm, 2.0 volt range
%DDDDDDDD = DAC level
for %TT = %01 and %MMMMM = %00000, %101_VV_xxxxSSSS = COG_DAC mode
%SSSS = Cog/streamer select: sets DAC level
for %00000 < %MMMMM < %00100 = SMART_DAC mode
DIR/IN are usual smartpin ctrl
%DDDDDDDD ignored, smartpin sets DAC level
for %MMMMM >= %00100 or (%TT = %1x and %MMMMM = %00000) = BIT_DAC mode
OUT sets DAC level, (ADC disabled?, IN = ?)
0: 0 = GIO level (revA) 0: %xxxxDDDD (revB) One of sixteen levels
1: %DDDDDDDD (revA) 1: %DDDDxxxx (revB) One of sixteen levels
0 5 0
%11_VV_CDDDDDDDD = COMP_DAC comparator mode, DAC always clocked (registered)
DIR enables PinA digital output
%VV = Comparator config
00: IN = PinA > D. PinA driven by 1k5R from OUT
01: IN = PinA > D. PinA driven by 1k5R from !IN
10: IN = PinB > D. PinA driven by 1k5R from IN
11: IN = PinB > D. PinA driven by 1k5R from !IN
%DDDDDDDD = DAC level for internal analogue compare
Mode list: Digital I/O (default)
- inversion
- clocking (registered)
- out strength
- feedback select
- logic in / schmitt in / comparator in
COMP_DAC
- preset level
- pinA/B in select
- feedback select (1500 Ohm out strength)
ADC_MODE
- in/gain select (pinB may be disabled in final silicon)
Updated 20-11-2018 to correct smartpin mode number for capturing ADC bitstream.
25-11-2018: Reverse the update. Doh!
26-12-2018: Typo, R -> % in BIT_DAC mode line. And stated for engineering sample.
26-3-2019: Add link to source "pin_modes.png" sheet.
3-7-2019: Add "0" and "5" bit position indicators for faster constant crafting.
17-11-2019: Add changed revB silicon BIT_DAC mode. Mark DAC_MODE and its sub-modes as registered I/O.
1-12-2019: Add short mode list.
We have the vastness of the internet and yet billions of people decided to spend most of their time within a horribly designed, fake-news emporium of a website that sucks every possible piece of personal information out of you so it can sell it to others. And they see nothing wrong with that.
A trap. Pnut has buggy code generation for PC-relative encoding of LOC instruction. Recommendation, for the moment anyway, is to always use #\ or #\@ .
NOTE: When specifying a numerical immediate in place of the label, it is treated as an absolute address and the same rules apply, ie: it will be encoded as PC-relative if possible, as per above. However, "@" produces an error with Pnut but not with p2asm. p2asm will perform a left shift by 2.
We have the vastness of the internet and yet billions of people decided to spend most of their time within a horribly designed, fake-news emporium of a website that sucks every possible piece of personal information out of you so it can sell it to others. And they see nothing wrong with that.
Beware the instructions TESTB & TESTBN seems to me to give the wrong results for the Z flags on P2-ES silicon
mov lmm_x,#0
testb lmm_x,#0 wz ' nz <<<<<
testbn lmm_x,#0 wz ' z <<<<<
testb lmm_x,#0 wc ' nc
testbn lmm_x,#0 wc ' c
and lmm_x,#1 wz ' z
mov lmm_x,#1
testb lmm_x,#0 wz ' z <<<<<
testbn lmm_x,#0 wz ' nz <<<<<
testb lmm_x,#0 wc ' c
testbn lmm_x,#0 wc ' nc
and lmm_x,#1 wz ' nz
For anyone new reading, all register-level instructions affect the Z flag as you would expect, where it gets set if the result was zero.
For the bit-level instructions, on the other hand, C and Z are both read and written directly, without semantic consideration for what Z usually means.
There's a couple of undocumented traps with the REP instruction. One was known and easy to avoid, the other was unknown until recently and should be corrected in the next revision.
The known one is the last instruction within the REP block cannot be a branching instruction. If a branching instruction is used then the branch distance will have the block length subtracted from it. It won't be remedied, so just don't do it.
Any intentionally cancelling branches must be at least one instruction back from the end of the REP block.
The until recently unknown one is with the Jxxx branch-on-event instructions. Using any of these instructions anywhere within the REP block will intermittently fail to branch upon event. When the failure occurs, execution will continue out the end of the REP block and even fail to take the first following branch.
We have the vastness of the internet and yet billions of people decided to spend most of their time within a horribly designed, fake-news emporium of a website that sucks every possible piece of personal information out of you so it can sell it to others. And they see nothing wrong with that.
New tidbit discovered: WRPIN input %AAAABBBB config still apply to IN when smartpin is off. Which presumably means %FFF config also works with smartpin off.
We have the vastness of the internet and yet billions of people decided to spend most of their time within a horribly designed, fake-news emporium of a website that sucks every possible piece of personal information out of you so it can sell it to others. And they see nothing wrong with that.
Code to convert a nibble to an ASCII character "0".."9" "A".."F" using just the C flag...
' convert nibble to ASCII hex char
getnib x,value,#7 '\ either instruction extracts only the lower nibble
and x,#$0F '/
or x,#"0"
cmp x,#";" wc ' : -> A etc
if_nc add x,#7
NOTE: This was fixed in v33 FPGA builds, rev B silicon doesn't have this flaw. It now forwards a copy of the write data so that the simultaneous read will see latest value.
There's a hardware flaw with shared lutRAM. A simultaneous read and write will glitch the read data. The glitch patterns change slightly with each run but the alignment doesn't. Same for different sys clock rates.
We have the vastness of the internet and yet billions of people decided to spend most of their time within a horribly designed, fake-news emporium of a website that sucks every possible piece of personal information out of you so it can sell it to others. And they see nothing wrong with that.
We have the vastness of the internet and yet billions of people decided to spend most of their time within a horribly designed, fake-news emporium of a website that sucks every possible piece of personal information out of you so it can sell it to others. And they see nothing wrong with that.
This isn't strictly a P2 trap, but the presence of the stack based RET instruction and relative branches makes this
much harder to spot and can lead you (well me) running around in circles trying to track down very strange behaviours
that seem to defy logic:
cognew (@entry, ...)
DAT
ORG
rogue long 0
entry ' your code
Of course the ORG should be after the random long data, otherwise all the addressing is thrown, but
the effect is to omit the leading instructions in call'ed routines (jmps are relative and tend to work),
which can have subtle and bizarre consequences.
On the P1 all branches get affected and things tend to fall over completely rather than limp along misbehaving
It would be nice to get an assembler warning if the first entry after ORG 0 is not an instruction.
There are times where the first instruction is a long that has no effect when executed.
P2 will be a little different to P1 in that you can start the cog at other than address COG $0, and also because we don't really have a NOP opcode.
Postedit
In P1 it was a common trick to use
LONG <somevalue>
where <somevalue> was <= 18-bits
This meant that the conditional bits EEEE=0000 and so the instruction would be treated as a NOP.
In P2 the EEEE=0000 is a valid instruction with the _RET_ condition. The instruction executes, and if not a taken branch it will also perform a RET instruction.
Therefore, any value other than all zeros will execute some intsruction.
Thus
LONG <any-non-zero-value>
will NOT BE TREATED AS A NOP in P2 !!!
Comments
P1 Prop OS (also see Sphinx, PropDos, PropCmd, Spinix)
Website: www.clusos.com
P1: Tools (Index) , Emulators (Index) , ZiCog (Z80)
P2: Tools & Code , Tricks & Traps
In P1...
LOCKSET D WC returns C=previous lock setting ie C=1=previously set
In P2
LOCKTRY D WC returns C=1=obtained lock Note the reversed result
This is opposite to P1, and also differs from
LOCKNEW D WC returns C=1=no lock available
P1 Prop OS (also see Sphinx, PropDos, PropCmd, Spinix)
Website: www.clusos.com
P1: Tools (Index) , Emulators (Index) , ZiCog (Z80)
P2: Tools & Code , Tricks & Traps
Here is the definitive example from Chip
https://forums.parallax.com/discussion/comment/1438380/#Comment_1438380
P1 Prop OS (also see Sphinx, PropDos, PropCmd, Spinix)
Website: www.clusos.com
P1: Tools (Index) , Emulators (Index) , ZiCog (Z80)
P2: Tools & Code , Tricks & Traps
Here is a solution to skipping instructions without using condition codes... Acknowledgement to PeterJ
P1 Prop OS (also see Sphinx, PropDos, PropCmd, Spinix)
Website: www.clusos.com
P1: Tools (Index) , Emulators (Index) , ZiCog (Z80)
P2: Tools & Code , Tricks & Traps
I noticed this very smart coding trick Chip used in checking for a hex character (0..9, A..F, a..f).
Code and table must be in cog.
I also found a shorter way to convert it to a binary nibble. The add x,#9 converts "A..F" to "J..O" and "a..f" to "j..o". "J" is ascii $4A and "j" is ascii $6A.
Then we strip off the upper nibble from both the "0..9" and "J..O"/"j..o" leaving $00..$0F"
While it's shorter code to first check for ASCII $80 and above, rather than using the 8 long table we could use a 4 long table. This would save 2 longs.
However, Chip is looking for the fastest code.
P1 Prop OS (also see Sphinx, PropDos, PropCmd, Spinix)
Website: www.clusos.com
P1: Tools (Index) , Emulators (Index) , ZiCog (Z80)
P2: Tools & Code , Tricks & Traps
The P1 ignores lowest hub address bit(s) for non-aligned hub reads and writes.
The P2 correctly accesses non-aligned word and long reads and writes.
In the P1, there were tricks associated with the lower hub address bit(s) being ignored. This code will fail on P2.
P1 Prop OS (also see Sphinx, PropDos, PropCmd, Spinix)
Website: www.clusos.com
P1: Tools (Index) , Emulators (Index) , ZiCog (Z80)
P2: Tools & Code , Tricks & Traps
In P1 we did this...
In P2 we do this...
And here is the trap: Our P1 code incorrectly converted to P2 Note we need to include the addct1 instruction into the loop because waitcnt no longer adds to the count
WAITX D/#
An alternative to just wait n+2 clocks is the WAITX instruction.
P1 Prop OS (also see Sphinx, PropDos, PropCmd, Spinix)
Website: www.clusos.com
P1: Tools (Index) , Emulators (Index) , ZiCog (Z80)
P2: Tools & Code , Tricks & Traps
P1 Prop OS (also see Sphinx, PropDos, PropCmd, Spinix)
Website: www.clusos.com
P1: Tools (Index) , Emulators (Index) , ZiCog (Z80)
P2: Tools & Code , Tricks & Traps
This should work. Just fill in the values and the values will be calculated for you...
(samples shown for P2-EVAL & P2D2 boards with 150MHz & 148.5MHz selected respectively) Oops. Fixed this _SETFREQ = 1<<24 +.. was 1<<25
Updated: 22 Feb 2019
P1 Prop OS (also see Sphinx, PropDos, PropCmd, Spinix)
Website: www.clusos.com
P1: Tools (Index) , Emulators (Index) , ZiCog (Z80)
P2: Tools & Code , Tricks & Traps
(Source - https://forums.parallax.com/discussion/comment/1392310/#Comment_1392310)
Mode list:
Digital I/O (default)
- inversion
- clocking (registered)
- out strength
- feedback select
- logic in / schmitt in / comparator in
COMP_DAC
- preset level
- pinA/B in select
- feedback select (1500 Ohm out strength)
ADC_MODE
- in/gain select (pinB may be disabled in final silicon)
DAC_MODE
- strength
- preset level
- COG_DAC
- - specify cog
- - cog/streamer provides level
- SMART_DAC
- - associated smartpin provides level
- BIT_DAC
- - preset split level
Updated 20-11-2018 to correct smartpin mode number for capturing ADC bitstream.
25-11-2018: Reverse the update. Doh!
26-12-2018: Typo, R -> % in BIT_DAC mode line. And stated for engineering sample.
26-3-2019: Add link to source "pin_modes.png" sheet.
3-7-2019: Add "0" and "5" bit position indicators for faster constant crafting.
17-11-2019: Add changed revB silicon BIT_DAC mode. Mark DAC_MODE and its sub-modes as registered I/O.
1-12-2019: Add short mode list.
P1 Prop OS (also see Sphinx, PropDos, PropCmd, Spinix)
Website: www.clusos.com
P1: Tools (Index) , Emulators (Index) , ZiCog (Z80)
P2: Tools & Code , Tricks & Traps
BMASK D,#11 'D = $FFF
BMASK D,#15 'D = $FFFF
BMASK D,#30 'D = $7FFF_FFFF
So here is another way of expressing this... adjusted per Chip's following suggestion
P1 Prop OS (also see Sphinx, PropDos, PropCmd, Spinix)
Website: www.clusos.com
P1: Tools (Index) , Emulators (Index) , ZiCog (Z80)
P2: Tools & Code , Tricks & Traps
Here is a section of code to record 'sample_length' of P0-P31 or P32-P63 using the streamer into HUB RAM at 'samples'...
P1 Prop OS (also see Sphinx, PropDos, PropCmd, Spinix)
Website: www.clusos.com
P1: Tools (Index) , Emulators (Index) , ZiCog (Z80)
P2: Tools & Code , Tricks & Traps
NOTE: When specifying a numerical immediate in place of the label, it is treated as an absolute address and the same rules apply, ie: it will be encoded as PC-relative if possible, as per above. However, "@" produces an error with Pnut but not with p2asm. p2asm will perform a left shift by 2.
Test code added: 2019-4-11
UPDATES:
2019-4-12: Bug is fixed in fastspin v3.9.25 - https://github.com/totalspectrum/spin2cpp
2019-4-13: Bug is fixed in p2asm v0.015 - https://github.com/davehein/p2gcc
P1 Prop OS (also see Sphinx, PropDos, PropCmd, Spinix)
Website: www.clusos.com
P1: Tools (Index) , Emulators (Index) , ZiCog (Z80)
P2: Tools & Code , Tricks & Traps
For anyone new reading, all register-level instructions affect the Z flag as you would expect, where it gets set if the result was zero.
For the bit-level instructions, on the other hand, C and Z are both read and written directly, without semantic consideration for what Z usually means.
The known one is the last instruction within the REP block cannot be a branching instruction. If a branching instruction is used then the branch distance will have the block length subtracted from it. It won't be remedied, so just don't do it.
Any intentionally cancelling branches must be at least one instruction back from the end of the REP block.
The until recently unknown one is with the Jxxx branch-on-event instructions. Using any of these instructions anywhere within the REP block will intermittently fail to branch upon event. When the failure occurs, execution will continue out the end of the REP block and even fail to take the first following branch.
This one is expected to be fixed. Example code of accommodating, and alternative below - https://forums.parallax.com/discussion/comment/1458393/#Comment_1458393
Some extra detail - https://forums.parallax.com/discussion/comment/1461458/#Comment_1461458
and https://forums.parallax.com/discussion/169438/rep-blocks-and-branching-issue/p1
P1 Prop OS (also see Sphinx, PropDos, PropCmd, Spinix)
Website: www.clusos.com
P1: Tools (Index) , Emulators (Index) , ZiCog (Z80)
P2: Tools & Code , Tricks & Traps
There's a hardware flaw with shared lutRAM. A simultaneous read and write will glitch the read data. The glitch patterns change slightly with each run but the alignment doesn't. Same for different sys clock rates.
v32i FPGA image on P123 board @ 20 MHz
RevA P2ES board @ 20 MHz
EDIT: Updated with extra tests. Thanks Brian.
EDIT2: Added that revB has this fixed.
You've managed to add a forth comment now!
Seem the forum software does not actually allow that, so I've done the next best thing..... ♪
much harder to spot and can lead you (well me) running around in circles trying to track down very strange behaviours
that seem to defy logic:
Of course the ORG should be after the random long data, otherwise all the addressing is thrown, but
the effect is to omit the leading instructions in call'ed routines (jmps are relative and tend to work),
which can have subtle and bizarre consequences.
On the P1 all branches get affected and things tend to fall over completely rather than limp along misbehaving
It would be nice to get an assembler warning if the first entry after ORG 0 is not an instruction.
P2 will be a little different to P1 in that you can start the cog at other than address COG $0, and also because we don't really have a NOP opcode.
Postedit
In P1 it was a common trick to use
LONG <somevalue>
where <somevalue> was <= 18-bits
This meant that the conditional bits EEEE=0000 and so the instruction would be treated as a NOP.
In P2 the EEEE=0000 is a valid instruction with the _RET_ condition. The instruction executes, and if not a taken branch it will also perform a RET instruction.
Therefore, any value other than all zeros will execute some intsruction.
Thus
LONG <any-non-zero-value>
will NOT BE TREATED AS A NOP in P2 !!!
P1 Prop OS (also see Sphinx, PropDos, PropCmd, Spinix)
Website: www.clusos.com
P1: Tools (Index) , Emulators (Index) , ZiCog (Z80)
P2: Tools & Code , Tricks & Traps