Pretty good weekend effort

nsaspook

Joined Aug 27, 2009
16,330
What I don't like is that there are those that insist there is no place for .asm in embedded processing, when I can demonstrate conclusively that there are cases where there is no place for C.
I agree totally within your restrictive constraints but those constraints are your own and are not general requirements for completion of a specific general programming task.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,305
I agree totally within your restrictive constraints but those constraints are your own and are not general requirements for completion of a specific general programming task.
No one writes embedded code for "general programming tasks" -- at least not for commercial products. That's what PCs and mobile devices are for. There is usually a specific end product (or family of products) with specific requirements in mind. And those requirements more often than not include such mundane things as "cost" and "power".

I don't work in academia or government. I don't have unlimited budgets, but I do have customers who expect a certain level of performance. My constraints are not self-imposed, but imposed upon me by my market.

The nice thing is: none of my competitors can do what I do at the cost I do it. This makes me highly competitive.
 

cmartinez

Joined Jan 17, 2007
8,768
We'll leave it at that. To be honest, I forgot what we were arguing about.
The way I see it, it's quite simple. If Joey expects to optimize to the maximum possible level the way he works personally, but die taking all of his knowledge with him, then he's on the right path. On the other hand, if he plans to either inherit or sell his business to someone else, then that poor fella is going to have the time of his life deciphering his life's work...

An exception to that former argument would be if he has employees who are knowledgeable about what he's doing ... but I doubt it.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,305
If Joey expects to optimize to the maximum possible level the way he works personally, but die taking all of his knowledge with him, then he's on the right path. On the other hand, if he plans to either inherit or sell his business to someone else, then that poor fella is going to have the time of his life deciphering his life's work...
Now wait a cotton pickin' minute:

Are you suggesting that my code is indecipherable because it is written in .asm? Or because I optimize my code to be fast and small?

I actually write good, clear, well commented, and easily maintainable code. In fact, I use exactly the same framework and methodology for all my projects (you'd be surprised how adaptable it is to any number of different kinds of applications). Once an experienced .asm programmer learns the framework, maintenance of any of my projects is easy.

Or are you suggesting I should hire (or replace myself with) inexperienced .asm programmers?

Now, the question is: do they still grow experienced .asm programmers? That is the important question.
 

cmartinez

Joined Jan 17, 2007
8,768
Or are you suggesting I should hire (or replace myself with) inexperienced .asm programmers?
Not. At. All. ... I did not know about your thoroughly documented code (though I guess I should've inferred that).

My most concise questions are:
  • Is your library easily readable by someone knowledgeable in assembly? (you've already answered "yes", so my question is moot)
  • Is your library structured in such a way that it would be not just easy to understand, but easy to remember its routines by an experienced programmer?
  • Would you consider your library a language on its own? Or does it share similarities in the way its called and invoked with other commercially available libraries?

End question:
No one lives forever. Is your plan to eventually sell your business to the best bidder? Or you don't know yet what you're going to do?

That last question is the heart of my curiosity ... you don't have to answer if you think it's inconvenient, of course.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,305
  • Is your library structured in such a way that it would be not just easy to understand, but easy to remember its routines by an experienced programmer?
  • Would you consider your library a language on its own? Or does it share similarities in the way its called and invoked with other commercially available libraries?
My libraries are typically fully encapsulated modules that already understand the framework they are being inserted into. I've explained this before (I think in my RN4020 thread).

Generally, a module is inserted into the framework by including two files in the build: module_name.inc and module_name.asm. Then, in the main program loop, a polling call is added: call pollmodule (and sometimes, promodule if the module needs to do additional data processing). The module -- as necessary -- automatically initializes itself (and the hardware it is controlling) and understands the various power states and modes defined by the framework. It instantiates its own interrupts as and if necessary. And, most importantly, it plays nice with all the other modules (i.e. cooperative multitasking).

Data and control are passed between modules via generic and application-specific function calls and call backs.

It's really, really easy to produce some very involved applications utilizing lots of different attached hardware once you get the hang of it.

My current project is up to 15K lines now, distributed throughout 63 total files in the build. So, on average, each file has only about 238 lines of code (of course, some more, some less and lots of comments). So the actual files are not all that complicated. The real trick is knowing which file(s) to operate on when you want to make a change.

No one lives forever. Is your plan to eventually sell your business to the best bidder? Or you don't know yet what you're going to do?
They're going to bury me at my desk. What else am I going to do? Hang out on AAC?
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,305
A good example:

In the code I posted in post #51, I included an LED module (led.inc, led.asm). In the main loop, there is a call to pollled.

At any time I wish, anywhere throughout my code, I can do something like:

Code:
    ledblink 1,1000,25        ;blink LED #1.  1s period, 25ms on-time

    ledon 2        ;turn LED #2 solid on

    ledoff 3        ;turn LED #3 off
That's it! The main app (and all the other modules) are completely oblivious that the LEDs are blinking/on/off or even present. The module just runs in the background and does whatever is necessary whenever it's necessary.
 

cmartinez

Joined Jan 17, 2007
8,768
I'm about to start learning pic-assembly for the PIC10LF322. Would it bother you if I tagged you every once in a while to ask for your help, Joey? ... I promise I'll do my homework and won't be a pest.
 

Thread Starter

joeyd999

Joined Jun 6, 2011
6,305
It's a matter of satisfaction.
More than you know.

Things just feel right when things have been abstracted properly -- and you know when something is missing.

For instance, I've got a module for the SPI peripheral. It manages the peripheral for all the other modules that use it (kinda like a primitive client/server approach). When the app requires a SPI transaction, it does something like:

Code:
dospi ADS1242_READ_DATA
and the SPI module takes care of things. It calls back, as necessary, to the target device module for things like SPI configuration and data to send/receive.

The problem is the SPI module requires each device and its commands to be enumerated (by me) in the SPI header (.inc) file. This breaks the abstraction and bugs the hell out of me.

It'd be nice if picasm were extensible. What I need, I think, to solve this problem, is a preprocessor that understands arrays.
 

nsaspook

Joined Aug 27, 2009
16,330
More than you know.

Things just feel right when things have been abstracted properly -- and you know when something is missing.

...

The problem is the SPI module requires each device and its commands to be enumerated (by me) in the SPI header (.inc) file. This breaks the abstraction and bugs the hell out of me.

It'd be nice if picasm were extensible. What I need, I think, to solve this problem, is a preprocessor that understands arrays.
I know the feeling.
That's a problem that HLL's like C were designed to solve in a human language understandable way. Obviously an asm programmer can do the same thing with proper coding on one machine instruction set at a time.

In C, the structure, pointer, array and other standard abstractions can be used to create functions that can easily dynamically configure devices or command sequences with the same code on just about any processor that supports a standard GCC like compiler.

On the riot-os port.
Here most of the configuration data is static to a set board configuration but it could have easily been R/W data structures set by a program instead of a 'const' array structure in flash memory.

Refactored the spi driver Init_Dma function for complete dynamic device configuration in partnership with the spi init and transfer code:
C:
    /**
    * @brief   SPI device configuration
    */
    typedef struct {
        volatile uint32_t *mosi_reg; /**< Output pin mux register address */
        volatile uint32_t *miso_reg; /**< MISO pin mux register address */
        uint8_t mosi_af; /**< Specify function of output pin */
        uint8_t miso_af; /**< Specify input pin for MISO */
        gpio_t mosi_pin; /**< GPIO pin for MOSI */
        gpio_t miso_pin; /**< GPIO pin for MISO */
    } spi_conf_t;

    typedef struct {
        volatile uint32_t *ipc_regset; /* interrupt controller SET register */
        uint32_t iec_mask; /* enables */
        uint32_t ifs_mask; /* flags */
        uint32_t ipc_mask_p; /* vector pri/sub-pri SFR masks and offsets */
        uint32_t ipc_mask_s;
        uint32_t ipc_mask_pos_p;
        uint32_t ipc_mask_pos_s;
    } dma_conf_t;

/** @} */

/**
* @name SPI device configuration
*
* @{
*/
#define SPI_NUMOF (3)

static const spi_conf_t spi_config[] = {
{
.mosi_pin = 0,
.mosi_reg = 0,
.mosi_af = 0,
.miso_pin = 0,
.miso_reg = 0,
.miso_af = 0
}, /* No SPI0 on PIC32, dummy to compile */

{ /*
* SPI 1 (MikBUS 1)
* MOSI -> RD3
* MISO -> RD14
* SCK -> RD1
*/
.mosi_pin = GPIO_PIN(PORT_D, 3),
.mosi_reg = (volatile uint32_t*) & RPD3R,
.mosi_af = OUTPUT_FUNC_SDO1,
.miso_pin = GPIO_PIN(PORT_D, 14),
.miso_reg = (volatile uint32_t*) & SDI1R,
.miso_af = INPUT_PIN_RPD14,
},

{ /*
* SPI 2 (MikBUS 2)
* MOSI -> RG7
* MISO -> RG0
* SCK -> RG6
*/
.mosi_pin = GPIO_PIN(PORT_G, 7),
.mosi_reg = (volatile uint32_t*) & RPG7R,
.mosi_af = OUTPUT_FUNC_SDO2,
.miso_pin = GPIO_PIN(PORT_G, 0),
.miso_reg = (volatile uint32_t*) & SDI2R,
.miso_af = INPUT_PIN_RPG0,
},

{ /*
* SPI 3 (MRF24WN0MA-1/RM100 - wifi module)
* MOSI -> RB9
* MISO -> RB10
* SCK -> RB14
*/
.mosi_pin = GPIO_PIN(PORT_B, 9),
.mosi_reg = (volatile uint32_t*) & RPB9R,
.mosi_af = OUTPUT_FUNC_SDO3,
.miso_pin = GPIO_PIN(PORT_B, 10),
.miso_reg = (volatile uint32_t*) & SDI3R,
.miso_af = INPUT_PIN_RPB10,
},
};
/** @} */

/**
* @name DMA device configuration
*
* @{
*/

#define DMA_NUMOF (8)
/* DMA [0..3] used for SPI ports 1 and 2 */
#define SPI1_DMA_RX 0
#define SPI1_DMA_TX 1
#define SPI2_DMA_RX 2
#define SPI2_DMA_TX 3

static const dma_conf_t dma_config[] = {
{
.iec_mask = _IEC4_DMA0IE_MASK, /* enable */
.ipc_regset = (volatile uint32_t*) & IPC33SET, /* IPC SFR */
.ipc_mask_p = _IPC33_DMA0IP_MASK, /* priority data mask */
.ipc_mask_pos_p = _IPC33_DMA0IP_POSITION, /* priority in SFR */
.ipc_mask_s = _IPC33_DMA0IS_MASK, /* sub-priority */
.ipc_mask_pos_s = _IPC33_DMA0IS_POSITION, /* sub-priority */
},
{
.iec_mask = 0, /* DON'T enable */
.ipc_regset = (volatile uint32_t*) & IPC33SET,
.ipc_mask_p = _IPC33_DMA1IP_MASK,
.ipc_mask_pos_p = _IPC33_DMA1IP_POSITION,
.ipc_mask_s = _IPC33_DMA1IS_MASK,
.ipc_mask_pos_s = _IPC33_DMA1IS_POSITION,
},
{
.iec_mask = _IEC4_DMA0IE_MASK,
.ipc_regset = (volatile uint32_t*) & IPC34SET,
.ipc_mask_p = _IPC34_DMA2IP_MASK,
.ipc_mask_pos_p = _IPC34_DMA2IP_POSITION,
.ipc_mask_s = _IPC34_DMA2IS_MASK,
.ipc_mask_pos_s = _IPC34_DMA2IS_POSITION,
},
{
.iec_mask = 0,
.ipc_regset = (volatile uint32_t*) & IPC34SET,
.ipc_mask_p = _IPC34_DMA3IP_MASK,
.ipc_mask_pos_p = _IPC34_DMA3IP_POSITION,
.ipc_mask_s = _IPC34_DMA3IS_MASK,
.ipc_mask_pos_s = _IPC34_DMA3IS_POSITION,
},
{
.iec_mask = 0,
},
{
.iec_mask = 0,
},
{
.iec_mask = 0,
},
{
.iec_mask = 0,
},
};
/** @} */

void spi_init_pins(spi_t bus)
{

    assert(bus != 0 && bus <= SPI_NUMOF);

    gpio_init(spi_config[bus].mosi_pin, GPIO_OUT);
    gpio_init(spi_config[bus].miso_pin, GPIO_IN);
    *(spi_config[bus].mosi_reg) = spi_config[bus].mosi_af;
    *(spi_config[bus].miso_reg) = spi_config[bus].miso_af;
}

static void Init_Dma_Chan(uint8_t chan, uint32_t irq_num, volatile unsigned int * SourceDma, volatile unsigned int * DestDma, spi_t bus)
{
assert(chan < DMA_NUMOF);

pic_dma[chan].regs = (volatile uint32_t *)(&DCH0CON + (chan * DMA_REGS_SPACING));
pic_dma[chan].bus = bus;

IEC4CLR = _IEC4_DMA0IE_MASK << chan; /* Disable the DMA chan interrupt. */
IFS4CLR = _IFS4_DMA0IF_MASK << chan; /* Clear the DMA chan interrupt flag. */
DMACONSET = _DMACON_ON_MASK; /* Enable the DMA module. */
DCHxCON(pic_dma[chan]) = 0;
DCHxECON(pic_dma[chan]) = 0;
DCHxINT(pic_dma[chan]) = 0;
DCHxSSA(pic_dma[chan]) = KVA_TO_PA(SourceDma); /* Source start address. */
DCHxDSA(pic_dma[chan]) = KVA_TO_PA(DestDma); /* Destination start address. */
DCHxSSIZ(pic_dma[chan]) = 1; /* default Source bytes. */
DCHxDSIZ(pic_dma[chan]) = 1; /* default Destination bytes. */
DCHxCSIZ(pic_dma[chan]) = 1; /* Bytes to transfer per event. */
DCHxECON(pic_dma[chan]) = irq_num << _DCH0ECON_CHSIRQ_POSITION; /* cell trigger interrupt */
DCHxECONSET(pic_dma[chan]) = _DCH0ECON_SIRQEN_MASK; /* Start cell transfer if an interrupt matching CHSIRQ occurs */
DCHxINTSET(pic_dma[chan]) = _DCH0INT_CHBCIE_MASK; /* enable Channel block transfer complete interrupt. */
/*
* set vector priority and receiver DMA trigger enables for the board hardware configuration
*/
*(dma_config[chan].ipc_regset) = dma_config[chan].ipc_mask_p & (SPIxPRI_SW0 << dma_config[chan].ipc_mask_pos_p);
*(dma_config[chan].ipc_regset) = dma_config[chan].ipc_mask_s & (SPIxSUBPRI_SW0 << dma_config[chan].ipc_mask_pos_s);
IEC4SET = dma_config[chan].iec_mask << chan; /* DMA interrupt enable if needed */
}

static void Release_Dma_Chan(uint8_t chan)
{
assert(chan < DMA_NUMOF);

IEC4CLR = _IEC4_DMA0IE_MASK << chan; /* Disable the DMA interrupt. */
IFS4CLR = _IFS4_DMA0IF_MASK << chan; /* Clear the DMA interrupt flag. */
DCHxCON(pic_dma[chan]) = 0;
DCHxECON(pic_dma[chan]) = 0;
DCHxINT(pic_dma[chan]) = 0;
}
The 'per' device device data board, function and interrupt data are contained in arrays of structures that can configure/reconfigure each spi bus channel to its dma channels on the fly. In an application program the same methods can be used with command and operations data functions.
 
Last edited:

Thread Starter

joeyd999

Joined Jun 6, 2011
6,305
In C, the structure, pointer, array and other standard abstractions can be used to create functions that can easily dynamically configure devices or command sequences with the same code on just about any processor that supports a standard GCC like compiler.
I know what you are saying, but you kinda missed my point.

I don't need dynamic structures (or even static ones!). I don't need the overhead of dynamic memory allocation. The hardware is fixed.

I just need an assembler pre-processor that can do more than just add and subtract integers and basic conditional assembly. Then I can write macros to build the structures for me at assembly time, abstracted away from the actual code.
 
Top