News:

PROTON pic BASIC Compilers for PIC, PIC24, dsPIC33

Main Menu

Aliasing PORT bits to variable bits ?

Started by trastikata, Feb 15, 2025, 08:25 PM

Previous topic - Next topic

trastikata

Hello all,

I am trying to think of a way to alias some port bits to variable bits.

To be more specific - I have a 18b RAM addressing using a 32b variable called RAM_ADDRESS, RAM_ADDRESS.Byte0 and RAM_ADDRESS.Byte1 are aliased to the 16b PORTB. However the upper two bits i.e. Bit16 and Bit17 are connected to PORTA.14 and PORTA.15

Is there a way, other than a Procedure, to alias those two PORTA bits with the Bit0 and Bit1 of RAM_ADDRESS.Byte2 so when the RAM_ADDRESS variable is written, the PORTA.14 and PORTA.15 are being changed together with the PORTB ?

What I currently have is this:

Dim DummyPort As PORTB.Byte0
Dim DummyPortH As PORTB.Byte1
Dim DummyPortHH As Byte
Dim DummyPortHHH As Byte
Dim RAM_ADDRESS As DummyPort.Dword

Main:
    Inc RAM_ADDRESS
    PORTA.14 = RAM_ADDRESS.16
    PORTA.15 = RAM_ADDRESS.17

Thus what I need, if it is at all possible, is bits of DummyPortHH.0 and DummyPortHH.1 to be aliased with PORTA.14 and PORTA.15 - I know this will not make the resulting Assembler code faster but it will make the program listing much neater and easier to manage and I don't have to write "PORTA.14 = RAM_ADDRESS.16" and "PORTA.15 = RAM_ADDRESS.17" in the program when variable "RAM_ADDRES" is changed.

trastikata

Not exactly the same as what I wanted, but with Macro's this achieves the neatness of the program listing without resorting to Procedure jumps thus by saving clock cycles for procedure jumps.

Device = 33EP256MU810
Declare Xtal = 160

Dim RAM_DATA As PORTD
Dim DummyPort As PORTB.Byte0
Dim DummyPortH As PORTB.Byte1
Dim DummyPortHH As Byte
Dim DummyPortHHH As Byte
Dim RAM_ADDRESS As DummyPort.Dword

$define WriteRam(Address,Data) RAM_ADDRESS = Address : PORTA.14 = RAM_ADDRESS.16 : PORTA.15 = RAM_ADDRESS.17 : RAM_DATA = Data : Low RAM_CE : High RAM_CE
$define ReadRam(Address) RAM_ADDRESS = Address : PORTA.14 = RAM_ADDRESS.16 : PORTA.15 = RAM_ADDRESS.17
$define SetRamWrite() High RAM_CE : Low RAM_WE : High RAM_OE : Output RAM_DATA
$define SetRamRead() Input RAM_DATA : High RAM_WE : Low RAM_OE : Low RAM_CE

Main:
    SetRamWrite()
    WriteRam(0x0010,0x1234)   
    WriteRam(0x0100,0x2345) 
    SetRamRead()
    ReadRam(0x0010)
    ReadRam(0x0100)
End 


Symbol RAM_CE = PORTG.1
Symbol RAM_WE = PORTG.0
Symbol RAM_OE = PORTF.13
Symbol RAM_LB = PORTF.12
Symbol RAM_UB = PORTF.8

Config FGS = GSS_OFF, GSSK_OFF, GWRP_OFF
Config FOSCSEL = FNOSC_FRCPLL, IESO_ON
Config FOSC = FCKSM_CSECME, IOL1WAY_OFF, OSCIOFNC_ON, POSCMD_NONE
Config FWDT = FWDTEN_OFF, PLLKEN_ON, WDTPOST_PS32, WDTPRE_PR128, WINDIS_OFF
Config FPOR = ALTI2C1_OFF, ALTI2C2_OFF, BOREN_OFF, FPWRT_PWR128
Config FICD = ICS_PGD2, JTAGEN_OFF, RSTPRI_PF
Config FAS = APL_OFF, APLK_OFF, AWRP_OFF

top204

#2
Because they are actually in different RAM address', they cannot be directly aliased to each other.

I have used the method you are using a few times in the past, and the compiler's LCD library subroutines use that method when the Data pins are assigned to different ports and pins.

Because a temporary Port. Pin change can have consequences if it changes, then changes again, the compiler creates the assembler code below for any SFRs (Special Function Registers) that are used as the assignment:

; rd_i000005_f001_000098_p000016,0 mkr$ in [test24.bas] PORTA.15 = RAM_dAddress.17
    btsc.b RAM_dAddressH,#2
    bset.b LATAH,#7
    btss.b RAM_dAddressH,#2
    bclr.b LATAH,#7

Notice that the Port is not changed while it is being checked whether it should be a 1 or a 0?

However, if the Port.Pins are not being used directly, so their temporary change of state does not matter, there is a faster way:

    PORTA.15 = 1
    If RAM_dAddress.17 = 0 Then
        PORTA.15 = 0
    EndIf

Which produces the tight assembler code:

    bset.b LATAH,#7
    btss.w RAM_dAddressH,#9
    bclr.b LATAH,#7

As can be seen, the state of the Port's pin is altered until it is known whether to change it or not, so it is not suitable for all situations. But for a synchronous interface, the pin's state is not read or transferred until another pin is toggled, so it works extremely well.

You could try the preprocessor meta-macros below, in place of yours:

$define WriteRam(pAddress, pData)                          '
    RAM_dAddress = pAddress                                '
    PORTA.14 = 1: If RAM_dAddress.16 = 0 Then PORTA.14 = 0 '
    PORTA.15 = 1: If RAM_dAddress.17 = 0 Then PORTA.15 = 0 '
    RAM_DATA  = pData                                      '
    PinClear RAM_CE                                        '
    PinSet RAM_CE
   
$define ReadRam(pAddress)                                  '
    RAM_dAddress = pAddress                                '
    PORTA.14 = 1: If RAM_dAddress.16 = 0 Then PORTA.14 = 0 '
    PORTA.15 = 1: If RAM_dAddress.17 = 0 Then PORTA.15 = 0

Also note the PinClear and PinSet commands instead of the Low and High commands?

The High and Low commands also alter the port's TRIS as an output when altering the pin's state, and this wastes time and code space. So as long as the pin has been configured as an output within an initialisation procedure, it will always be an output, and just setting the pin's state or clearing the pin's state is a fast, single mnemonic, operation.

The PinHigh and PinLow commands come into their own when a pin has to be changed from an input to output state, then the TRIS alteration is a must. The original High and Low commands were created like that because most new users of microcontrollers do not know what the TRIS SFR is for, or forget to alter it. I did that mistake myself in the beginning.

Note the PinHigh and PinLow commands are the same as the High and Low commands (renamed as additional commands), but are clearer to undestand in a program's code listing.

top204

#3
One thing to note with the above post...

If anyone is using one of the newer 18FxxQ71 devices, all of the above tight assembler code gets blown away, because the idiots at microchip have placed the PORT, TRIS, and LAT SFRs in a RAM bank that is not bankless! Why??

For the entire history of the 18F devices, the important, and commonly used, SFRs have been in the SFR section of Access RAM, so they are fast to operate and completely bankless. But for some 'inexplicable' reason, they are no longer fast to operate and require RAM bank changing for reading and writing a port?

I would not touch a new 18FxxQ71 device with a barge pole, or any of the newer 18F devices that have the PORT, TRIS and LAT SFRs out of SFR Access RAM and are placed in RAM bank 1, and I hope microchip recognise their stupid mistake and correct it, as they did with the early enhanced 14-bit core devices. Because the code required just to read or write a port has increased, and for something that requires fast reactions, this is a very stupid thing to do.

trastikata

Thank you Les. I am using DSPIC33EP256MU810 so this doesn't apply.

But I came across a problem with a variable even/odd address alignment, so I had to change the Dword port aliasing. Anyway with 2 additional cycles, the code works.

top204

#5
Aliasing with the 16-bit devices has to be done carefully, and with knowledge of the PIC24 and dsPIC33 arcitecture, otherwise an "odd address" exception will occur, because multi-byte variables that use the Word orientated assembler mnemonics must be placed on a 16-bit RAM boundary with the 16-bit devices, which the compiler does invisibly to the user.

For example, aliasing a multi-byte variable to a Byte variable or Byte array or an uneven part of a multi-byte variable or SFR will cause problems 1 out of 2 times, and the device will create an exception interrupt (a reset if a trap procedure has not been placed in the program listing), because the compiler aligns multi-byte variables and arrays (Dword, Float, Word) on a 16-bit RAM address boundary, but Byte variables are created in RAM after those and on any boundary because it does not matter with them or the Byte oriented mnemonics, and saves lots of RAM space that would have been wasted because gaps do not need to be placed between them to line up Byte address' on a 16-bit RAM boundary. Also, some parts of a multi-byte variable are in none 16-bit address aligned RAM, so will cause the problem.

So if a multi-byte variable or array is aliased to a Byte or Byte array, or a segment of a multi-part variable, it will cause problems. i.e. The High byte of a Word variable, the .Byte1 of a Dword or Float variable, and .Byte3 etc, because .Byte0 and .Byte2 sit on a 16-bit RAM boundary, but .Byte1 and .Byte3 do not.

I have trapped some of these in the compiler as errors, but it cannot trap every instance of an alias.