-
December 28th, 2008, 04:40 PM
#1
Reading/Writing to H/W registers
I need to read and write to H/W register. Of course, I'm given an address and the representation for the data. For instance at address 0x00009 the layout is as follows:
Spare : 16;
Revision : 8;
Number : 8
To structure this in code, my current thought surrounds the use of Sets and Gets within a class. Bit Fiddling is done within the Sets and Gets to allow the user to retrieve the desired element. Now given:
Code:
class FPGA_Memory_Map {
public :
void Set_Revision ( unsigned int Revision ) {
// do some bit fiddling within a local variable - a shadow register of sorts
// write value to the register location
}
unsigned int Get_Revision ( ) {
// get value from register location
// update the local variable
return ( whatever ) ;
}
// lots more gets/sets
};
At issue: I'll need to have a Write and Read method in every Set and Get respectively. The Read and Write methods provides the interface to the H/W register. Clearly this is starting to look like a recipe for disaster especially when each bit within a 32 bit value means something different: i.e
MBC1 : 1
MBC2 : 1
MBC3 : 1
// up to
MBC32 : 1
The question, how can I structure the application such that when the user calls a Set/Get methods the Write/Read methods gets invoked?
Maybe a different approach would be prudent. I'm open to alternatives.
Thanks
-
December 28th, 2008, 05:10 PM
#2
Re: Reading/Writing to H/W registers
1) Under MOST operating systems you can NOT directly write to hardware registers.
2) If you can infact write to them, then Placement New can be your friend.
TheCPUWizard is a registered trademark, all rights reserved. (If this post was helpful, please RATE it!)
2008, 2009,2010
In theory, there is no difference between theory and practice; in practice there is.
* Join the fight, refuse to respond to posts that contain code outside of [code] ... [/code] tags. See here for instructions
* How NOT to post a question here
* Of course you read this carefully before you posted
* Need homework help? Read this first
-
December 28th, 2008, 11:17 PM
#3
Re: Reading/Writing to H/W registers
Originally Posted by TheCPUWizard
1) Under MOST operating systems you can NOT directly write to hardware registers.
Agreed, except my application is executing on a TI DSP, which has a litany of peripheral registers - SPI, CAN etc. etc. + my own memory map - to deal with.
Originally Posted by TheCPUWizard
2) If you can infact write to them, then Placement New can be your friend.
In which case, I could use it within the set and get methods?
-
December 29th, 2008, 12:49 AM
#4
Re: Reading/Writing to H/W registers
Typically in C, you would use macros to construct the proper pointer casting of the fixed memory address for reading a writing the register. In C++ you can use inline template functions instead of macros. For example:
Code:
#include <iostream>
using namespace std;
template <typename register_t, typename ptrdiff_t>
inline
volatile register_t* reg_ptr(ptrdiff_t address)
{
return reinterpret_cast<register_t*>(address);
}//reg_ptr
template <typename register_t, typename ptrdiff_t>
inline
register_t reg_read(ptrdiff_t address)
{
return *reg_ptr<register_t>(address);
}//reg_read
template <typename register_t, typename ptrdiff_t>
inline
void reg_write(register_t value, ptrdiff_t address)
{
*reg_ptr<register_t>(address) = value;
}//reg_write
int main()
{
// use memory off the heap for testing
int *p = new int;
int addr = reinterpret_cast<int>(p);
// write to "register" then read it back
reg_write(5, addr);
cout << reg_read<int>(addr) << endl;
// or, create a reference variable representing the "register"
volatile int ®_ref = *reg_ptr<int>(addr);
reg_ref = 0;
cout << reg_read<int>(addr) << endl;
delete p;
return 0;
}//main
gg
-
December 29th, 2008, 11:00 AM
#5
Re: Reading/Writing to H/W registers
You CAN to the casting, but then it gets lost with other casts. Pleacemet new overcomes this...
Code:
struct RegBlock
{
BYTE RegA;
Byte RegB;
}
void *regBase; // Initialized to base address of this registerr group...
RegBlock *p = new(regBase) RegBlock();
p->RegA = 123;
TheCPUWizard is a registered trademark, all rights reserved. (If this post was helpful, please RATE it!)
2008, 2009,2010
In theory, there is no difference between theory and practice; in practice there is.
* Join the fight, refuse to respond to posts that contain code outside of [code] ... [/code] tags. See here for instructions
* How NOT to post a question here
* Of course you read this carefully before you posted
* Need homework help? Read this first
-
December 29th, 2008, 12:40 PM
#6
Re: Reading/Writing to H/W registers
Codeplug & CPUWizard thanks alot. Sure helps alot when I see code
Truly appreciate it. If I could apply a rating I would. Not sure why this 'low level' (though I admit I'm not a fan of) stuff had me stuck on trying to determine what's the right thing to do
I might be back for a critique of my approach when I put something together based on ideas presented.
Last edited by mop65715; December 29th, 2008 at 12:42 PM.
-
December 29th, 2008, 12:44 PM
#7
Re: Reading/Writing to H/W registers
Originally Posted by mop65715
Truly appreciate it.
You can...just click on the little "scales" icon to the right of the replies...
Also dont forget to mark threads as resolved...
TheCPUWizard is a registered trademark, all rights reserved. (If this post was helpful, please RATE it!)
2008, 2009,2010
In theory, there is no difference between theory and practice; in practice there is.
* Join the fight, refuse to respond to posts that contain code outside of [code] ... [/code] tags. See here for instructions
* How NOT to post a question here
* Of course you read this carefully before you posted
* Need homework help? Read this first
-
December 29th, 2008, 01:55 PM
#8
Re: Reading/Writing to H/W registers
I don't see how anything is "lost" using either method. Placement new achieves the same results as a cast.
When mapping a structure of contiguous registers onto memory, you must take care to ensure the compiler generates the proper byte-packing and alignment. You should also generate volatile reads and writes when accessing the registers.
Thinking about it more, this seems like the most straight forward way of accessing a single register or a block of registers (in my opinion):
Code:
typedef unsigned long uint32; // 32bit type on "this" platform
volatile uint32 ®1 = *reinterpret_cast<uint32*>(/*0x9*/new uint32);
struct RegBlock1 // ensure proper packing/alignment
{
uint32 REG1;
uint32 REG2;
};//RegBlock
volatile RegBlock1 ®_BLOCK1 = *reinterpret_cast<RegBlock1*>(/*0x9*/new RegBlock1);
If you have a lot of (unrelated) registers/blocks to define, you could organize them into separate namespaces for additional readability.
You can also check your compiler documentation to see if it supports bit-field access to individual registers. For example: http://focus.ti.com/lit/an/spraa85a/spraa85a.pdf
gg
-
December 29th, 2008, 02:19 PM
#9
Re: Reading/Writing to H/W registers
Originally Posted by Codeplug
I don't see how anything is "lost" using either method. Placement new achieves the same results as a cast.
Let me e-mail you code that has the following characterisicws.
1) 100,000 line of source code
2) 2,000 reinterpret_casts in the code base.
3) 5 structures that represent "fixed address" items (e.g. registers)
4) 75 places in the code where these structures ARE mapped to the physical location.
5) a couple of hunder of these same structures that are in normal ram (ie shadows)
Your task is to find the 75 locations in the code. You have 30 minutes.
With your approach it would be virtually impossible.
With placement new. You can just put a private override of the placement new operator in the 5 structures, re-compile and hacve the answer in about a minute (assuming a really fast compiler!)
That is what gets "lost".
TheCPUWizard is a registered trademark, all rights reserved. (If this post was helpful, please RATE it!)
2008, 2009,2010
In theory, there is no difference between theory and practice; in practice there is.
* Join the fight, refuse to respond to posts that contain code outside of [code] ... [/code] tags. See here for instructions
* How NOT to post a question here
* Of course you read this carefully before you posted
* Need homework help? Read this first
-
December 29th, 2008, 03:01 PM
#10
Re: Reading/Writing to H/W registers
Those 75 places would be qualified with volatile, and the "shadows" wouldn't.
gg
-
December 29th, 2008, 03:07 PM
#11
Re: Reading/Writing to H/W registers
Originally Posted by Codeplug
Those 75 places would be qualified with volatile, and the "shadows" wouldn't.
gg
Most likely not...the general accepted pattern is to make the members volatile, as shown in the code snipped below. Requiring the "user" of the structure to remember to declare each instance volatile violates the principle
Originally Posted by Scott Meyers
Make classes easy to use properly and difficult to use incorrectly
Code:
struct RegisterSet{ enum eCommands { CommandAddress = 1,
CommandDisable = 2,
CommandIdle = 3,
CommandNone = 0,
CommandNop = 7,
CommandReceive = 4,
CommandTransmit = 5 };
enum eStates { StateDisabled = 0,
StateIdle = 1,
StateMaster = 2,
StateSlave = 3,
StateSlaveTerminated = 4,
StateTransition = 5 };
unsigned : 6;
bool volatile const TransmitDataFull : 1;
bool volatile const ReceiveDataAvailable : 1;
bool volatile const Interrupt : 1;
bool volatile const Acknowledged : 1;
eStates volatile const State : 3;
eCommands volatile Command : 3;
};
TheCPUWizard is a registered trademark, all rights reserved. (If this post was helpful, please RATE it!)
2008, 2009,2010
In theory, there is no difference between theory and practice; in practice there is.
* Join the fight, refuse to respond to posts that contain code outside of [code] ... [/code] tags. See here for instructions
* How NOT to post a question here
* Of course you read this carefully before you posted
* Need homework help? Read this first
-
December 29th, 2008, 03:25 PM
#12
Re: Reading/Writing to H/W registers
>> the general accepted pattern
That pattern suggests that the structure is not meant to be used in ram. Otherwise the "shadow" copies would be unnecessarily qualified as volatile.
gg
-
December 29th, 2008, 03:31 PM
#13
Re: Reading/Writing to H/W registers
Originally Posted by Codeplug
>> the general accepted pattern
That pattern suggests that the structure is not meant to be used in ram. Otherwise the "shadow" copies would be unnecessarily qualified as volatile.
gg
volatile only has a potential for negative impact if there was the opportunity for the compiler to optimze multiple reference (ie by using a register).
In 30+ years of professional development, including many real-time (embedded system) programs where I am concered with sub 100uS timing loops), I have NEVER encountered a single situation where this has occured.
It is much better to write robust code that minimizes the risk of errors. Unless it can be PROVEN (for a pspecific circumstance that there is no viable alternative the safest code is always the best code.
TheCPUWizard is a registered trademark, all rights reserved. (If this post was helpful, please RATE it!)
2008, 2009,2010
In theory, there is no difference between theory and practice; in practice there is.
* Join the fight, refuse to respond to posts that contain code outside of [code] ... [/code] tags. See here for instructions
* How NOT to post a question here
* Of course you read this carefully before you posted
* Need homework help? Read this first
-
December 29th, 2008, 05:06 PM
#14
Re: Reading/Writing to H/W registers
There only ever needs to be one instance of "RegisterSet" per hardware instance (eg. their may be multiple UARTs). Making that one instance volatile qualified isn't any more or less "robust" than using explicit qualification with multiple instances (created with placement new). Since the one instance is already volatile - there's no way for the "user" to make a mistake - they just use the already defined instance. The only advantage that could be claimed in using one method or the other, is that shadows wouldn't be unnecessarily volatile qualified - which ties the optimizers hands in more ways than just cpu-register allocation. Still, I don't see one as being more/less robust than the other.
Having a single instance means no need for placement new. Searching the source for non-shadowed references is as simple as searching for "REG_BLOCK1" (using my previous example). Still, I don't see anything being "lost" choosing this approach over the other.
If you wanted to support shadowed copies of "RegBlock1", then you would need to add copy and assignment operators:
Code:
#include <iostream>
using namespace std;
//------------------------------------------------------------------------------
typedef unsigned long uint32; // 32bit type on "this" platform
struct RegBlock1 // ensure proper packing/alignment
{
uint32 REG1;
uint32 REG2;
RegBlock1() {}
RegBlock1(volatile const RegBlock1 &rhs) {*this = rhs;}
RegBlock1& operator=(volatile const RegBlock1 &rhs)
{
REG1 = rhs.REG1;
REG2 = rhs.REG2;
return *this;
}//assignment operator
};//RegBlock1
extern volatile RegBlock1 ®_BLOCK1; // for header
#define REG_BLOCK1_ADDR /*0x9*/ (new RegBlock1) // use heap for testing
volatile RegBlock1 ®_BLOCK1 = *reinterpret_cast<RegBlock1*>(REG_BLOCK1_ADDR);
//------------------------------------------------------------------------------
int main()
{
// write to "REG1" then read it back
REG_BLOCK1.REG1 = 4;
cout << REG_BLOCK1.REG1 << endl;
// create a non-volatile shadow copy of block 1
RegBlock1 rb1_copy = REG_BLOCK1;
return 0;
}//main
This gives you another method for finding all shadow copies in the source: just comment out the copy/assignment operators and compile.
gg
-
December 29th, 2008, 05:45 PM
#15
Re: Reading/Writing to H/W registers
Originally Posted by Codeplug
There only ever needs to be one instance of "RegisterSet" per hardware instance (eg. their may be multiple UARTs). Making that one instance volatile qualified isn't any more or less "robust" than using explicit qualification with multiple instances (created with placement new). Since the one instance is already volatile - there's no way for the "user" to make a mistake - they just use the already defined instance.
This is self conflicting. If there are multiple dynamic instances (for example the Photon Dispersion Energy controller I wrote earlier this year had between 16 and 256 sensors depending on configuration), and it was a different developer than the original author of the structure who instantiated the instances putting them into a collection.
"Steve" was a user of the classes that "Robert" designed, and he NEVER had to use or think about the word volatile. "Peter" used the instantiated instances (exposed via STL collections) and never had to know about placement new. Complete seperation of concerns.
I am NOT disagreeing with your methodology in terms of it working. There is no argument there. And my approach is definately biased by the fact that I run a small development company, where a single "oops" can mean the difference between success and company failure (which has not happened in the 25 years in business)
TheCPUWizard is a registered trademark, all rights reserved. (If this post was helpful, please RATE it!)
2008, 2009,2010
In theory, there is no difference between theory and practice; in practice there is.
* Join the fight, refuse to respond to posts that contain code outside of [code] ... [/code] tags. See here for instructions
* How NOT to post a question here
* Of course you read this carefully before you posted
* Need homework help? Read this first
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|