Real Time Analysis of an Engine Management System

Real Time Analysis of an

Engine Management System

 

 

 

 

Completed for ENGG 442 (Real-time Systems) by:

Ben Beacock

Andrew MacArthur

Ron Tezuka


Table of Contents

 


1. Introduction/Background

                The rapid development of control system technology has drastically changed automotive systems.  The first electronic fuel injection systems were used as far back as the 1960’s but relied on analog technology.  ‘Transistorized’ ignition systems using hall effect sensors instead of breaker switches were common by the early 1980’s.   The first completely electronic engine management system in the Volkswagen lineup was introduced in 1988.  The Digifant system was developed by Bosch in Germany.  It featured electronic injectors with a batch fired fuel injection system and an ignition control system integrated together.  Around 1993, sequential injection systems were becoming more common.

 

Throttle Body vs Batch fired vs Sequential Injection

                Early engines used a carburetor, which is a fairly simple design.  As air passes through a venturi, the vacuum sucks the fuel into the air, which is calibrated by the size of the opening.  This proved to be a very reliable system as there are no electronics, just fuel passageways that occasionally get clogged.  However, it was difficult to adjust for varying conditions, such as a cold engine at startup and there was a lag if the throttle was depressed too quickly.  These were addressed with a choke and acceleration pump, but were simple methods of dumping alot of fuel into the air and were not very efficient.  Throttle body injection is a direct replacement for the carburetor.  In place of the venturi(s) there is an injector to supply fuel to the entire engine.  Fuel is constantly metered to the amount of air entering the engine.  Batch fire injection takes this a step further by placing an injector in the intake path of each cylinder.  For a four-cylinder engine, there are 4 injectors.  This is also referred to as port injection, as there is one injector for each intake port and evens the amount of fuel each cylinder receives.  Because the timing is independent of engine rotation, the fuel may reach the intake valve when it is closed.  All of the fuel is eventually vapourized and enters the cylinder, but may not happen until after it is needed.

                Sequential injection takes port injection to the next level.  Each injector is specifically timed to only be open when its corresponding cylinder is on its intake phase.  This allows more precise metering of fuel because the majority is injected into the engine and not on the back of the intake valve.

 

EFI332 Development

                In 1995 a project was spawned from the do-it-yourself electronic fuel injection (DIY-EFI) mailing list to create an engine management system based on the Motorola 68332 32-bit processor.  The system that was designed is a CPU board and separate I/O board with the signal conditioning hardware.  An updated version was designed in 1999 by Bruce Bowling and Al Grippo, with a 4 layer PCB to reduce electromagnetic interference (EMI) and both boards integrated into one.  The 4-layer board has been used successfully to run an 8-cylinder Camaro engine.  Their code is used as a basis for this system analysis.

 

2. Motivation

                There are still many older vehicles in use with the Digifant engine management system.  However, this system uses batch-fired injection and does not utilize an accurate method for determining the engine position.  A very restrictive sensor is used to measure the mass airflow where it is more efficient to use speed-density calculations instead.  A newer system that could directly replace the existing computer would easily provide more power, and better fuel economy.

                The aftermarket tuning community has very little option for tuning these engines for racing.  The EPROM in the existing computer can be replaced, but they are not tuned specifically for that car and engine setup.  The other option is to fit an aftermarket engine management system, which are very expensive and require an new wiring harness to be built.  Aftermarket systems are also very generic so that they can support 8 to 12 cylinders, which decrease their efficiency in a 4-cylinder engine.

 

3. Problem Statement 

Our goal is to model a real-time engine management system.  This purpose of this system is to increase the overall performance of the engine by increasing the average power outputted by the engine and to reduce fuel consumption, which in turn increases the mileage per gas-tank.  This system must be safe, reliable and economically viable for the average car owner.

 

3.1 Constraints

·         Must use existing processor board

·         Must support serial communications to a PC application

This application will receive vital engine parameters, and can modify any variables in the controller memory for engine tuning.

·         Controls engine ignition timing, and fuel injection

The fuel injection is sequential electronic, so that fuel is timed to each cylinder.

The ignition timing is referenced from an RPM vs. load 2-dimensional map

·         Uses existing fuel delivery system components

·         Must be adaptable to other VW engines with re-tuning of parameters

·         All tasks must be schedulable within one engine cycle

·         Maximum engine RPM of 10000 (6ms per cycle)

 

3.2 Criteria

·         Must approach hard real-time response as close as possible

Soft real-time response is acceptable, but decreases efficiency

·         low cost to produce (maximize profit)

·         reliable

·         safety

·         easy to upgrade software

 

4. Specifications

4.1 Engine

Displacement: 1981cc

Sensors

                Manifold Air Pressure +1bar to –1bar (0 - 5V)

                Throttle Position (0 – 5V)

                Coolant Temperature NTC (resistive)

                Manifold Air Temperature (0 - 5V)

                Camshaft Position, variable reluctance w/ 52 tooth w 1 missing

                Ignition trigger: Hall effect, 2 pulses per engine revolution

Fuel Delivery System

                Pump system pressure: 75psi

                Pressure regulator: 36psi

                Injectors

                               Type: Saturated

Flow Rate @ full open: 205 cc/min

                               Impedance: ~4 ohms each

 

4.2 Processor Board

Processor (Motorola MC68332ACFC20)

                Processor Clock: 20 MHz  

                Debugging: on-chip Background Debugging Module (BDM)

Memory

                NV SRAM (ST M48Z128Y-70PM1) x 2

                               Memory Size: 128K x 2 = 256K

Access Time: 70 ns

                               Memory Block Location: 0x80000 – 0xC0000

                Flash ROM (ST M29F010B – 70K6) x2

                               Memory Size: 128K x 2 = 256K

Base Location: 0x00000

Analog/Digital (Motorola MC145051P) x 2

                Type: Successive Approximation

Resolution: 10-bits

Communication: serial, direct connection to 68332 SPI

Speed: 20.4k samples /sec, 44us max conversion time

Sensor Conditioning (National LM1815N) x 2

                Input: Variable reluctance sine wave or Hall effect signal

                Output: 0 – 5V square wave

Serial Communications (Maxim MAX232CPE)

Power Supply (SG7805P)

                Input: 12V noisy (8V – 16V)

                Output: 5V Stable

 

5. Usability

         Usability is a measure of the quality of the user experience when interacting with something whether it is a web site, software application or any other device that can operate in some way or another. It makes sense to talk about the usability of a shower. Can a person immediately figure out how to turn on the shower? When the shower is turn to full blast does water still come out?

 

The usefulness of a system is determined by two components: 

 

1.        Utility: Does the system do something that people care about? Does the system solve the main problem? If the system does not solve the main problem then what does it matter if its easy to use. It will be a poor system.

2.        Usability: Is the system hard to use, and can the user use it effectively? Even if the system solves the problems of the user, if they cannot use it then it will be a poor system.

 

Usability is not a single number, but has five characteristics:

 

1.        Ease of learning: How fast can a user, who has never seen the user interface before, learn it sufficiently well to accomplish basic tasks?

2.        Efficiency of use: Once an experienced user has learned to use the system, how fast can he or she accomplish tasks?

3.        Memorability: If a user has used the system at some earlier date, can he or she remember enough to use it more effectively next time (or does the user have to start over again learning everything every time)?

4.        Error frequency and severity: How often do users make errors while using the system, how serious are these errors (burning down a cement plant is worse than getting the wrong player's score on a golf site), and how easy is it to recover from a user error?

5.        Subjective satisfaction: How much does the user like using the system?

 

In any system all five characteristics should be considered, but there are always some characteristics that are more important. In the fuel injection system the two most important characteristics would be the efficiency of use and error frequency and severity. The reason why these are so important is because the system has to be soft real time, which means that the user can accomplish a major of their tasks, which means when the user pushes down on the throttle, more fuel is put into the system in a timely manner such that the car will still be functional. The engine should always have enough fuel to run the car.  In terms of error frequency and severity, does an error in the software cause the car to stop running? Also, if there is an error in the software are there backups that will stop the software from setting certain values above critical levels. An example would be not allowing the fuel injectors to open. Therefore, overall the system is very usable because the fuel injector system is hidden from the user. When the user presses down on the throttle they do not care about how the injector system works, but they do care if the system allows the car to keep running.

 

                This system will have a graphical user interface to make engine tuning much easier.  The code to make the interface and the calculations are all hidden from the user.  Everything is kept simple to make the system easy to use.  If the user only sees and can modify only the variables that need to be modified, and if even those variables are scanned by the software for values exceeding predefined values, then the software is even more user friendly.  Scanning for extreme values keeps a user from possibly inadvertently destroying or damaging the engine. 

 

                The system will be easy and convenient to use.  This ease of use will make the system efficient for both the user and the system.  The system will have a connector that will allow it to plug directly to a PC or Laptop via an external cable.  Then the software, once started, will interface directly with the MC68332 and the user will then be able change the values that are modifiable.  The software will concentrate on all other tasks.

                This system will have user guides during each step of the engine tuning and extensive help menus to let the user know the meaning of each variable, what the limitations are, how to use the system (a guide), the calculations behind the software (in case the user wants to know what’s going on), etc.  In the future the system will be easier to come back to at any time because the system has high usability. 

 

                This system will be ‘paraded’ or ‘tested’ by many users to get a subjective opinion of the system as a whole.  Influential parameters such as ease of learning, aesthetics of the graphical user interface, ease of set-up, support (help menus), and other consumer traits will be evaluated.

 

6. Functionality

6.1 General Parameters Needing Update (Soft Real-time, priority based)

Calculate RPM

                Worst Case Calculation (Best acceleration of engine):

                1st gear acceleration,  4000RPM to 10000RPM in approximately 1 seconds

 

                Maximum RPM/dt  = 6000RPM / 1 second

 

                at 10000 rpm, cylinders firing every 3ms.

 

                Maximum RPM change per firing cylinder = 6000RPM/sec x 0.003 sec = 18 RPM

 

The TPU automatically updates the HL_PRD (high word, period) in the TPU memory with the last period calculated for a tooth.  This task generates a RPM value based on the number of teeth in the wheel and the time base for the tooth period.  A large percentage of the engine management calculations are in some way based on the RPM calculation, most importantly the spark lookup table and the volumetric efficiency.  Because of this, it is assigned a medium priority and shorter update period.

 

Read and Convert Throttle Position

Throttle position can change from open to close in less than half a second.  It is the user that controls and modulates the amount of power the engine produces.  The only positions of importance are full throttle and closed throttle.  Full throttle signals the system to create maximum power rather than optimize for efficiency.  Closed throttle enables the idle control circuit to keep the engine running at low speeds.  This sensor needs to be updated regularly for a precise time derivative calculation.

 

Calculate Throttle Position Time derivative

A high rate of change in the throttle position requires adjustments to the fuel mixture to decrease the latency of the system response.   

 

Read and Convert Manifold Air Pressure

A change in manifold pressure is a direct result of the throttle opening or closing.  It is used in both ignition and fuel calculations and is therefore the most important sensor value along with RPM.

 

Calculate Manifold Air Pressure Time derivative

This value can be used to calculate the acceleration enrichments if the throttle position sensor stops functioning.

 

Read and Convert Coolant Temperature

The Coolant Temperature sensor signal is used mainly to calculate the warm-up enrichments during the initial running of the engine up to normal operating temperature.  The coolant acts as a heat reservoir, therefore the temperature of the engine does not change rapidly. The time response of this signal is several orders of magnitude slower than the engine response and therefore the task has a low priority and a long update period.

 

Read and Convert Manifold Air Temperature

In a normally aspirated engine (not turbo or supercharged), the intake air comes directly from the outside environment.  The air is heated slightly due to the heat of the engine, but the rate of change is still very slow.

However, in turbocharged or supercharged applications the temperature of the air heated directly by compression.  Since boost pressure can change drastically, a higher priority should be assigned to this sensor. 

 

Read and Convert Battery Voltage

Battery voltage can fluctuate due to heavy loads and can spike when high amperage devices, such as headlights, are turned on.  The alternator attempts to keep the battery at a constant voltage by constantly charging it through a voltage regulator.

 

Read and Convert Barometric Air Pressure

Many fuel injection algorithms simply use a programmed value for this sensor because it is mainly dependent on altitude above sea level, and only slightly different due to the weather.  The rate of change is very small and can be measured in minutes, so the priority of reading this sensor is extremely low.

 

Calculate Battery Voltage Compensations (from Battery Voltage)

The fuel injectors are flow rated based on a normal voltage.  If the voltage is increased, the injector will open faster, allowing slightly more fuel in for the same pulse width.  Compensations for the voltage to the fuel pulse width attempt to reduce these errors. 

 

Calculate Air Density (from Air Temperature and Barometric Pressure) 

The density of the air is not a rapidly changing value and a default value can be used if the air temperature or barometric pressure tasks are not schedulable.

 

Calculate warm-up enrichment (Coolant temp), acceleration enrichments (TPS dt) 

The warm-up enrichment component is only entered during the first 5 to 10 minutes of the initial system execution when the engine is not at normal operating temperature.  The calculations are quite extensive to determine the proper fueling at this point.  They are also a critical requirement during this stage because engine emissions testing is performed at this point.  The engine is normally in full operation while still in this range, therefore these tasks must be included as worst case analysis.

The acceleration enrichment task is very time dependent.  It uses the time derivative of the throttle position sensor to detect a sudden increase in power required.   This task is only triggered when the derivative reaches a defined level.  Because it can happen at any time, it is included as a worst case scenario.

 

Serial Data Updates (very low Priority)

Update LCD engine information

                Output vital engine data constantly to the LCD.

Or Output Telemetry to PC

                Output vital engine data every time a frame request character is received.

Input variables from PC

                Receive data from the serial port and store in the specified memory locations.

 

6.2 Per Cylinder Calculations (Hard Real-time) 

Period = 3ms

deadline = 6ms per cycle/4 cylinders x 2 revolutions per cycle = 3ms @ 10000rpm, 60ms @ 500rpm

 

FUEL

Rev Limiter

Fuel is cut on alternating cylinders when the RPM is at a critical level. This prevents engine damage if it is pushed beyond its limits.

 

Volumetric efficiency correction 3D map (MAP, RPM)

This task has a variable time, based on the lookup function.  The volumetric efficiency 3D map requires 2 index values, which are found by 2 index arrays. It is optimized to start looking at the top of these arrays, where the higher RPM and MAP values would be when the time is critical.  For real-time calculations, the worst case is assumed even though the deadline time increases as the RPMS drop.  Further modeling of this relationship would provide a more precise relationship.

There are 20 array locations available in each array, so the worst case would touch all 20 in each array before finding the 2 values.

 

Mass air flow (air density, MAP, vol efficiency correction)

                The amount of air entering the cylinder is calculated in mass of air per cylinder. 

 

Enrichment Compensations (Mass Air Flow, Warm/Acc Enrichments)

                All of the fuel adjustment factors are combined.

 

Calculate fuel required (mass airflow, enrichment compensations)

The normal ratio of air to fuel is 15 : 1 by mass.  This is not always the best ration based on certain conditions and the enrichment compensations attempt to optimize this ratio for efficiency.

 

Calculate pulse width (battery compensations, fuel required)

Using the flow specifications of the fuel injector, the amount of fuel for the cylinder can be converted to a length of time to turn the injector on.

 

Calculate pulse width start time (rpm->airspeed adjustment)

The start time of the fuel pulse must coincide with the opening of the intake valve for the current cylinder.

 

Update TPU

The start time of the pulse and the length of the pulse are sent to TPU memory so that the TPU can update the function for the current cylinder.

 

IGNITION

Spark 3D map (MAP, RPM)

This task is almost identical to the Volumetric Efficiency task in function and priority.  The timing of the spark is relative to the piston reaching its highest point and is therefore independent of which cylinder it is.

 

Calculate current cylinder firing time

The relative firing time is converted to a time relative to the start of the engine cycle for the specific cylinder.

 

Update TPU

The calculated time is written into the TPU memory so that the TPU can update the function for the current cylinder.

 

7. Reliability

You're driving down an interstate highway in the dead of winter and it's 20 miles to the nearest town. As you drive into the night, you ask yourself: What would I do if the fuel injector system stop working? Could I walk that far in sub-zero temperatures? Do I trust my car that much? For most people the answer would be “yes”. This is where words like "dependable" or "trustworthy" come from. This is what is meant by reliability.  Reliability is the probability of failure free operation for a specified time in a environment for a given purpose. Another way of thinking about reliability is how well the system users think it provides the services they require.

 

Below are some dos and don’ts for reliability:

 

DO…

·         Understand interaction failure modes.

·         Be aware of program timing.

·         Understand what customers expect.

·         Use common systems and methods.

·         Communicate with other engineers.

·         Track vendor performance.

·         Design for easy assembly.

·         Consider external influences.

·         Regard reliability as a moving target.

 

DON'T…

·         Invent during the product development process.

·         Focus too narrowly and forget how your component interacts with others.

·         Think your job has stopped after the product is launched.

 

From  http://www.manufacturing.net/magazine/dn/archives/1997/dn1006.97/19f8996.htm

Also, reliability can be divided into many parts. Below is a table showing how the reliability of a system can be measured.

Major Division of Reliability Methods

System Reliability
(Reliability Prediction)

Probability methods are used to determine system reliability using knowledge of system architecture and part reliability.

Part Reliability
(Reliability Estimation)

Statistical methods are used to determine part reliability using part failure data (reliability data). A part is an item that is assumed to be indivisible for the purpose of determining its reliability

Repairable System Reliability

Use of reliability figures of merit that require some knowledge of system repair activity.

Non-Repairable System Reliability

Use of reliability figures of merit that do not require knowledge of repair activity (if any)

 

From http://web.utk.edu/~leon/rel/overview/reliability.html

 

                The question that needs to be answered is how reliable is the fuel injection system in terms of the software within the processor. Everyone has herd the expression “To err is human.” and since the programmer is a human there is always going to be some type of error associated with the software. To guard against programming errors there will be extensive testing done on the fuel injector to make sure it conforms to automobile standards.  In order for the EFI system to be considered reliable it must be able to handle the different stresses and conditions that are inflicted on an automobile regularly.

 

Cars face temperatures from –70F in northern Canada to 130F in Arizona desert. Owners drive them over gravel roads and bumps and they are oblivious to car repairs. The fuel injector system will have to guarantee that all tasks are completed within the specified time while in various road conditions. Therefore, the fuel injector software will be reliable. This will be proved by long and continuous hours of testing, in all possible conditions and stipulations. 

 

                With respect to the fuel injection system, this system is particularly reliable.  Each component within this system has it’s own reliability and failure rate.  These can be combined to produce a general reliability and failure rate.  However, this rate would only be applicable to a perfect system.  The fuel injector system can run under less perfect conditions or with one or more parts not functioning.

 

                The system has good reliability and the severity of most errors is low.  If any of the sensors or signal conditioners attain failure state, the system can recover from these up to a predefined limit.  Though the probability of multiple sensor and/or signal conditioner failures causing system failure are astronomically small.  The system will likely fail upon processor failure or power failure.  Other failures will cause a loss of efficiency and power in most cases.

 

                Suppose a sensor is supposed to send data to the micro-controller every 500 nanoseconds.  This data is then converted and used to adjust the fuel flow within this system. Now suppose that due to cold weather or some other outside factor, that this sensor sends data at a rate of once every microsecond or two.  This slower rate affects how fast the fuel flow is changed since the corrective data is coming in at a slower rate, however it will not prevent the car from working on it’s own.  This is due to the fact that the MCU adjusts the fuel flow based on many different sensors and data reading.  This even allows for a complete failure of one or two sensors, (depending on the importance of the respective data) and still obtain a normally running engine (normal as defined by standard assumptions by car drivers.  I.e. does the car vibrate, make loud noises, jerk when shifting gears, etc).  The fuel injector system is still affected by part failures, but many of these failures do not cause catastrophic effects upon failure.  Each of these will instead reduce the efficiency of the fuel injector system, which will in turn cause lower power output and fuel conservation.  There are still components which will affect the system and cause serious problems such as if the power source to the system is cut off, or either of the onboard processors fails, or if the controller for the fuel injector throttle position fails.   Each of these could potentially shut down the system and leave the consumer stranded in the middle of nowhere.  The reliability of parts is also directly associated with costs, and this will be discussed in the next section.

 

8. Minimize Cost, Maximize Profit

         In any firm or business the goal is always to maximize profit while minimizing cost.  The fuel injection system is no different. The choice of the programming language as well the compiler and operating system will help determine whether the system will produce an economic profit. The fuel injector system needs to have a programming language, compiler, operating system, programmers and debuggers. Each has a cost associated with it and the wrong choice could put your company in the ‘red’.

 

                One of the hardest choices when developing a system is which programming language to choose whether it is C, Java, Visual Basic, Ada or C++. In choosing a program language you as the developer have to decide which language will give the best functionality. The optimal solution is that all the tasks can be completed with one language because this will increase portability, maintainability and at the same time increase profits. Another reason to choose a programming language to build your system is dependent on how previous systems were developed. Did they succeed? because in the business world a measure of success is economic profit.

 

                The language that the fuel injector system is implemented in is ‘C’.  A main advantage ‘C’ has over languages like visual basic it is free. Also, the compiler that ‘C’ uses is also free. The only costs our group can see in the development of the fuel injector system are the testers, operating system and the programmers themselves.

               

Below is a table showing the cost associated with each element for the EFI system

Table 1.

Resource

Product

Cost

‘C’ – Programming language

ANSI C

Free

‘C’ – Compiler

GNU C

Free

Operating System

RTEMS(no support)

Free

Support for RTEMS

US$4500, 3 engineers

Programmers

EFI Software

CAN$60 / hour development

Testers

Engine Dynamometer

CAN$100 / hour rental

 

         The cost associated with the hardware of the system is dependent on a couple factors.  The needed reliability of the parts, the minimum lifetime of each part (within a specific statistical distribution), and the operating conditions of the parts (which may also affect the materials and manufacturing process of each part). 

 

                Some of the parts are fixed and do not have any trade-off’s associated with them.  This includes the MC68332, the fuel injector itself (although this may be changed through a co-production with the car company itself), and power supplies. 

 

                The costs of many of the sensors, wiring, controllers, and regulators do have trade-off’s associated with them.  In the process of choosing parts and minimizing costs, the following criteria should be taken into consideration.

 

1.        Operating range.  Does this part need to be used in –150 degree weather or would a cheaper version that will operate in –40 degrees or warmer weather suffice?

2.        Does the part need a lifetime of 50 years when that car itself will only last 10 – 15 years?  This doesn’t even include the many cars that will be destroyed through accidents.  A cheaper part that will last a little over the car’s lifetime will fit the criteria.

3.        Does the part need to be made of certain materials or to be made with the absence of other materials?  Such as will Ferro-magnetic materials interfere with the processor and other integrated circuits?  Does the part need aluminum casings for heat sinks?

4.        Do highly accurate readings need to be taken from certain parts?  Will a lower accuracy from a less important sensor reading affect the overall efficiency by 0.1 %?  0.01 %?

5.        Will the parts have to be interchangeable?  Are the parts easy to change by the user or will the car have to be taken in to a qualified technician?

 

Each of these criteria will affect the costs of the individual part and the overall system.  A decision matrix would be the best and easiest way to choose parts based on the weighting of each criteria.

 

9. Portability

9.1 Software Portability

         No matter what the programming language is chosen to produce the system it has to be portable.  Portability is the ease with which software can be transferred from one computer system or environment to another (IEEE Dictionary 1984).  Portability means that the system can be move from one operating system to another and still have the same functionality.  Another way of thinking about portability is if adapting it to a new environment is easier than rewriting it for that environment than the system is portable. Therefore, portability promotes code reuse because the system can be used again and again to perform different functionality. Most portability problems are not pure language issues. Portability involves hardware (byte order, device I/O) and software (utility libraries, operating systems, run-time libraries).

 

                The language chosen to implement the fuel injection system is ‘C’. One of the main advantages of ‘C’ is that it is very portable. It can be used on Linux and Windows.  In ‘C’ the precision and storage for the basic data types (short, int, float, double, etc.) is different for each implementation of the system. This is a major source of porting problems when moving from one system to another because changes in numeric precision can affect calculations and assumptions about the size of structs can be violated. In a language like Java an int in one of systems is the same size on every other system. There is no arbitrary pointer arithmetic and no assumptions about struct packing, which can lead to non-portable coding practices.

 

                In the ‘C’ language it is therefore important to pay attention to word sizes because data types like pointers are not always the same size as ints or other pointers. Below is a chart with some ‘C’ data types and their size and a comparison to other systems:

 

 

 

  type     pdp11  VAX/11   68000  Cray-2  Unisys  Harris      80386

          series          family            1100    H800

  _________________________________________________________________

  char         8       8       8       8       9       8          8

  short       16      16    8/16  64(32)      18      24       8/16

  int         16      32   16/32  64(32)      36      24      16/32

  long        32      32      32      64      36      48         32

  char*       16      32      32      64      72      24   16/32/48

  int*        16      32      32  64(24)      72      24   16/32/48

  int(*)()    16      32      32      64     576      24   16/32/48

 

As you can see there are some differences which may cause the program to fail or do unexpected things.

Also, some machines have more than one possible size. Therefore, a programmer wants to use data types, which are going to be the same on a majority of the systems. This leads to portable code. Here is a chart below showing all the ‘C’ safe types:

 

   Type    Minimum   No Smaller

            # Bits      Than

  _____________________________

  char           8

  short         16      char

  int           16     short

  long          32      int

  float         24

  double        38     float

  any *         14

  char *        15     any *

  void *        15     any *

 

 

The programming code that was written for the fuel injector system uses a lot of pointers and structures. A few portability problems first being that different compilers have different conventions for returning structures. This causes a problem when libraries return structure values to code compiled with a different compiler. Also, there may be unused holes in structures. Below is a few samples of how the structures and pointers were used in the EFI software:

 

 

 

 

 

Structures:

 struct constants {                   /* Constants */      short int sci_baud_pc;      char lcd_c1[8],lcd_c2[8],lcd_c3[8],lcd_err[7];    };

Pointers:

                *(vp.pm_ptr + vp.sci_ix)

 

Also, when doing an embedded system, it is always better to separate the system into machine dependent and independent code. Thus, if the system is ever moved to a new machine it will be much easier to determine what changes need to be made. Machine dependence in the headers of the appropriate files.

 

Another important point on portability is there are some things that are inherently non-portable. Example would be code that is designed for a particular piece of hardware, such as drivers. Therefore, it’s best to make your code portable first and worry about efficiency later. This decreases the amount of development time for a product.

 

Some porting problems in the ‘C’ language are the existence of maximum and minimum values for certain types, the character order in memory, whether the character type has signed or not, how values are converted to different types, properties of pointers and the difference between different implementations of ‘C’.

 

Overall, the fuel injector system is pretty portable because the code was written in ‘C’, but because the language has a lot of non-portable techniques such as structs and pointers it decreases the overall portability of the system.

 

9.2 System Portability

                The system will be designed and prototyped on a Volkswagen 1.8L 16V engine, but it is possible to adapt the constants in memory to work on any normally aspirated, 4 cylinder engine.  The sensors also need to be compatible, with a 0-5V output to work with the A/D converter.  There are no hard coded constants in the source so that it is possible to change during engine tuning. 

               

Ignition systems, however, can have different forms.   The traditional method of ignition is a single coil with a distributor to choose which spark plug receives the spark.  The distributor removes any dependency of the ignition controller for determining which spark plug to fire.  There are two other methods for firing individual spark plugs: waste fire and individual coils.  Waste fired uses one coil per pair of cylinders.  On a 4-cylinder engine, there are 2 cylinders with their pistons at the top of the stroke and 2 at the bottom when a spark fires.  Only one of the cylinders at the top has a compressed charge ready for spark, but it does not harm the engine to fire the other one simultaneously.   This eliminates the need for a distributor but requires 2 timing signals.  The final method uses one coil per cylinder, but the cost of coils and the added complexity of 4 timing signals adds too much cost to the system.

 

10. Safety

When one thinks of safety it usually means that nothing bad will happen, but safety can also be divided into more technical terms such as progress and security. These mean that nothing has the right to happen and things will happen under proper authorization. Each of the three terms (safety, progress, security) combine to form a more boarder sense of the word “safe”.

 

Assuming all stages of the software development have been done such as analysis, specification and design stages, programming safety critical systems must be done with extreme discipline. If the system is to be “safe” the programmer must have a limited expressive freedom (e.g. ‘C’ is not a good language to code safety – critical systems) and document the code so that it is easy to read as possible. Also, extreme care must be taken to keep the use of pointers and recursion (tree structures usually use pointers and recursion) to a minimum and to prevent numeric under and over flow.  Therefore, if the system to be developed should have a language that has strong type checking so that data types are kept to a minimum.

 

                Sometimes accidents occur, such as when a hardware device fails and the software cannot stabilize the situation. This is why it is important that your software system be prepared for all types of scenarios. Although because of other constraints such as money and time and depending on how critical the error, detection techniques may be left out.  In order to have your system considered “safe” the following analysis should be done; software fault tree analysis, formal methods, human-computing interfaces, programming languages and techniques and various standards.

 

                In order to effectively analyze a system, software fault analysis (SFTA) has to be done. SFTA is derived from previous hardware model and the two can be combined to provide a clear analysis of the potential hazards associated with the system. SFTA also shows that a fail-safe system may have to compromise reliability in order to stabilize the hazardous situation. For example, in a nuclear power plant it might be better to shut the core down if any bad input is detected and cannot be dealt with, rather than risk a meltdown at a later stage when unforeseen events might eventually compound into an accident. SFTA starts with the hazard and then traces back to possible causes of the hazard, this is called a bottom up process. To represent this or and and logic gates are used combined with symbols that represent different types of events and faults. These events and faults are connected by lines, which show the flow of logic.  Once the tree has been completely specified for any fault and sub-faults, the associated risk can be identified and action can be taken.

 

One of the most known safety critical systems is Ada, which was developed by Department of Defense (US) for mission critical, embedded software systems. The reason Ada was developed was because the US department wanted software that was safer, cheaper and more reliable. Ada’s most important safety feature is the exception handling ability, which is required for fail-safe manufacturing processes. Also, the ability to reuse code and information hiding make that more formidable.

 

11. Maintainability

Maintainability: According to Goldman and Slattery, a qualitative definition of maintainability is given as "... the characteristics (both qualitative and quantitative) of material design and installation which make it possible to meet operational objectives with a minimum expenditure of maintenance effort (manpower, personnel skill, test equipment, technical data and maintenance support facilities) under operational environmental conditions in which scheduled and unscheduled maintenance will be performed."  While a quantitative definition of maintainability is introduced as "... maintainability is a characteristic of design and installation which is expressed as the probability that an item will be restored to specified conditions within a given period of time when maintenance action is performed in accordance with prescribed procedures and resources."

 

                The mechanisms necessary to support maintainability are also the same needed for reusability. If software is highly modularized and consistent specifications are used then the maintainability will increase.

 

To improve maintainability follow simply programming rules such as do not hard code numbers directly in expressions so that if the programmer left and someone else had to update the system they would not be confused about what the numbers mean. Also, don’t use preprocessor for defining complex macros. In ‘C’ the operation of preprocessor is poorly defined in the C coding standards, and how the preprocessor operates is at the mercy of the compiler writer. The preprocessor is useful and if applied properly can increase the readability and maintainability of the code.

 

                The ‘C’ programming is naturally maintainable because of its relative closeness to the human language, but because of pointers and other data types which causes a ‘C’ program to be harder to maintain. Since the fuel injector system uses ‘C’ and ‘C’ is a naturally maintainable language the maintainability should be good, but because of pointers and machine dependant code is mix with non-machine dependant code the maintainability is average.

 

 

 

12. Efficiency

Efficiency is the balance between the form of an object and its function. If the form of an object (like a building or a car) is well suited to its function, then that object is said to be highly efficient. A system is therefore considered when an optimum balance is found between minimizing the costs and maximizing the effects/quality.

 

An example of efficiency is; have you ever heard someone talk about cars in terms of fuel efficiency? A car that is designed to travel more miles on a gallon of gasoline (or is rated at a higher number of miles per gallon) is higher in efficiency because it saves the driver money.

 

                A worker may spend a substantial portion of his life waiting for computer program to produce output. Organizations and users control this wait time by purchasing addition memory, faster computers, or using faster network connections. The developer has a responsibility to make programs the best use these limited and expensive resources.

 

                The most important optimization technique is to use a profiler to identify performance bottlenecks. If you base your optimization efforts on speculation instead of using a profiler, a person will waste a lot of time speeding up a process in your program that was already fast. Once the bottleneck has been identified, for example a loop that executes thousands of times, redesign the program so that it doesn’t need to execute the loop a thousand times. This is more effective then making the loop run 10% faster, which is exactly what a optimizing compiler can do. Optimization is a waste of the programmer time if any of the following statements are true:

 

·         parts of the program haven't been written yet

·         the program is not fully tested and debugged

·         it seems to run fast enough already

 

Also, the user should keep in mind how the program will be used because if the program is only going to be run once or twice a day then optimizing the code is really not an issue. However, if the system is a real time system, then the program should be optimized as much as possible. This is why the time(1) command should be used to check if the program is computer-bound, memory bound, or I/O bound. Even if the program seems to be taking a long time to run you getrusage() to get performance information.

 

                The following paragraph will give sample code showing how ‘C’ code can minimize time spent by the CPU to improve memory and I/O speed improvements.

 

1) LOOP UNROLLING

Many compilers (e.g. gcc -funroll-loops) will do this, but if you know that yours doesn't you can change your source code a bit to get the same effect.

Old code:

  for (i = 0; i < 100; i++) 

  {

      do_stuff(i);

  }

New code:

  for (i = 0; i < 100; ) 

  {

      do_stuff(i); i++;

      do_stuff(i); i++;

      do_stuff(i); i++;

      do_stuff(i); i++;

      do_stuff(i); i++;

      do_stuff(i); i++;

      do_stuff(i); i++;

      do_stuff(i); i++;

      do_stuff(i); i++;

      do_stuff(i); i++;

  }

 

                The new code will get executed 11 times rather than the 101 times that the old code will execute. This is called loop unrolling. This method works best when the loop is executed a fixed non-prime number of times and the iterative value is only modified in one place (aside from initialization).  If the function do_stuff() did not require ‘i’ then ‘i++’ could be replaced by a single ‘i +=10’. Also, rearranging the for loop into a do-while loop can decrease the amount of execution from 11 to 10. 

 

2) LOOP JAMMING

                The idea behind loop jamming is to combine adjacent loops that loop over the same range of the same variable. Assuming nothing in the second loop indexes forward (for example array[i+3]), you can do this:

Old code:

  for (i = 0; i < MAX; i++)   /* initialize 2d array to 0's */ 

      for (j = 0; j < MAX; j++)

          a[i][j] = 0.0;

  for (i = 0; i < MAX; i++)   /* put 1's along the diagonal */

      a[i][i] = 1.0;

New code:

  for (i = 0; i < MAX; i++)

  {

      for (j = 0; j < MAX; j++)

          a[i][j] = 0.0;      /* initialize 2d array to 0's */ 

      a[i][i] = 1.0;          /* put 1's along the diagonal */

  }

 

                The new code increments and test i half as much. In certain circumstances, locality of reference may be better to improve cache behaviour.

 

3) STRENGTH REDUCTION

                Strength reduction involves replacing an expression by a different expression that will yield the same value, but in a more efficient manner. Most compilers will do this automatically. The classic examples:

Old code:

  x = w % 8;

  y = pow(x, 2.0);

  z = y * 33;

  for (i = 0; i < MAX; i++) 

  {

      h = 14 * i;

      printf("%d", h);

  }

New code:

  x = w & 7;             /* bit-and cheaper than remainder */ 

  y = x * x;             /* mult is cheaper than power-of */

  z = (y << 5) + y;      /* shift & add cheaper than mult */

  for (i = h = 0; i < MAX; i++)

  {

      printf("%d", h);

      h += 14;           /* addition cheaper than mult */

  }

 

                An important note and that is array indexing is basically a multiply and a add. The multiple part can be subjected to strength reduction especially when looping through an array.

 

4) MEMORY LEAKS

                In ‘C’ the commands malloc and free are used to allocate and de-allocate memory. There is only one problem with them and that they are often implemented completely different on different machines. In some applications malloc takes very little time, but free takes a lot of time. Therefore, if your program uses a lot of memory, but doesn’t reuse any of it before it dies, then the program may run faster if this is done in a header file someplace:

 

        #define free(x)   /* no-op */

 

                Programs that use a significant portion of memory should free it as soon as they are done using the space.

 

13. Testability

The testability of software is defined as the probability that a piece of software will fail on the next execution during testing if the software includes a fault. Software testability focuses on the probability that a fault will be revealed within a program. For years computer scientists have been trying to answer the question " what is the probability that this code will fail?" Testability on the other hand asks a different question " what is the probably this code will fail if it is faulty?" Another way of thinking about testability is if software faults were gold and software testing is gold mining then software testability would be the geologists survey to predict the probability of finding gold in certain locations. It is not the geologists job to dig for gold. A geologist might say, "This valley may or may not have gold, but if there is gold, it will be in the top 50 feet, and it will be all over this valley." At another location, the geologist might say, "Unless you find gold in the first 10 feet on this plateau, there is no gold.

 

The IEEE Standard Glossary of Software Engineering Terminology (1990) defines testability as:

"(1) the degree to which a system or component facilitates the establishment of test criteria and the performance of tests to determine whether those criteria have been met, and (2) the degree to which a requirement is stated in terms that permit

establishment of test criteria and performance of tests to determine whether those criteria have been met."

 

In order to determine the degree a person must first determine the test criteria and hence testability is a simply measure of how hard it is to satisfy a particular testing goal. Common examples of testing goals are coverage and complete fault eradication. An input distribution is required for testability, but it is not a requirement and any statistical prediction must first include an assumption about the distribution of inputs used during operation.

 

                The ‘C’ language provides two facilities that make testability easier. They are assertions and conditional compilation. An assertion is a Boolean expression as its parameter. The assert program evaluates an expression and then exits with an error message if the statement is false. The conditional compilation directive lets a person include or exclude code based on conditional statements. This allows a programmer to put in special debugging statements that can be removed by changing a single global value.

Below is an example of how an assert statement can be used:

 

        assert( ( "Assertion Message", x == y ) );

 

If for some reason the assertion fails then it will print (“Assertion Message”, x == y)' along with the values for line number and filename.

 

Rule:

You should use assertions throughout your code to check degenerate and "impossible" conditions. You should also use assertions to check the sanity of parameters input to a function.

 

Assertions sometimes clutter up the listing and therefore should not be used, but an editor like Codewright can remove lines containing assertion statements if this is a problem. Overall, the advantages of assertions far outweigh the disadvantages.

 

                Assertions allows a programmer to help protect against disastrous conditions, but when an assertion fails the program stops. Sometimes a programmer would like the program to continue executing (when an assertion fails) and output the information to a log file. This is where conditional compilation statements such as #ifdef and #ifndef directives to include or not include debugging code.

 

Rule:

All debugging code must disappear if the symbol "NDEBUG" is defined. That is, you must surround all debugging code with "#ifndef NDEBUG" and a corresponding #endif.

 

The symbol “NDEBUG” eliminates all the assertions in the program and therefore eliminates all the debugging code if it is defined. Unlike assertions, a programmer may not want all debugging statements active at one time. For example, if there are five sets of debugging statements in the code the programmer may only be interested in one set or the other. The solution is to associate the current debug block

and use a nested #ifdef statement to activate or deactivate the debugging code. Consider the following code:

 

 

    #ifndef NDEBUG    /* To turn off debugging for production code */

    #ifdef DebugCalc

 

        fprintf(debugLogFile,"Appropriate Logging Output\n");

 

    #endif

    #endif

 

Again a programmer shouldn’t use #ifndef and #ifdef because they feel it will make their program hard to read because there is the Codewright editor that automatically removes #ifdef / #endif code that wouldn't normally be compiled.

 

14. System Analysis

To schedule the tasks on the processor, the period and computational time of each task was needed.  Some way of measuring the actual time for each process would produce the most accurate results.  This would require including breakpoints in the software and timing, which was determined to be beyond the scope of this project.  Instead, the code was compiled from C to assembly and the number of instructions for each function was determined.  Any iterations were taken into consideration and the maximum number used.  The int_2_float and float_2_int functions are used throughout the main program and are fairly long so they were also considered.  The ‘number of instructions’ were multiplied by an average number of cycles per instruction and the period of each cycle.  The memory read and write times are close enough to the bus cycle time to assume 0 wait states for the memory.  A value of 10 cycles per instruction was estimated.

Table 1.  Software Analysis

 

Priority

Start Instr

End Instr

I2F

F2I

Loop time

Instructions

Time(ns)

CPU Cycles

General Parameters Needing Update

 

 

 

 

 

 

RPM

 

699

719

 

 

 

20

11921

200

TPS/dt

 

719

784

2

1

 

354

211003

3540

TPS/dt

 

784

829

 

 

 

45

26822

450

MAP

 

829

867

1

1

 

223

132920

2230

MAP/dt

 

867

901

 

 

 

34

20266

340

CTS

 

901

936

 

 

 

35

20862

350

MAT

 

936

963

 

 

 

27

16093

270

Volts

 

963

1003

1

1

 

225

134112

2250

BAP

 

1003

1043

1

1

 

225

134112

2250

Volt compensation

 

1065

1105

 

 

 

40

23842

400

Air Density

 

1105

1137

2

 

 

240

143053

2400

Enrichments

 

1137

1899

1

1

 

947

564463

9470

 

 

 

 

 

 

 

 

1439471

 

Serial Data Updates (very low Priority)

 

 

 

 

 

 

 

 

 

update LCD

 

1899

2412

 

1

84

594

354056

5940

Output to PC

 

 

 

 

 

 

 

 

 

Input from PC

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Per Cylinder Calcs

 

 

 

 

 

 

 

 

 

FUEL

 

 

 

 

 

 

 

 

 

Rev limiter

 

2416

2476

1

 

 

164

97753

1640

Fuel Pulsewidth

 

 

 

 

 

 

 

 

 

Volumetric efficiency

 

2476

2731

4

 

570

671

399952

6710

Mass air flow

 

2731

2774

2

 

 

251

149610

2510

Enrichment Compensations

 

2774

2783

 

 

 

9

5364

90

Calc fuel required

 

2781

2821

2

 

 

248

147821

2480

Calc pulsewidth

 

2821

2969

3

1

-35

541

322465

5410

Calc pulsewidth start time

 

2969

3117

1

1

 

333

198486

3330

Update TPU

 

3117

3156

 

 

 

39

23246

390

IGNITION

 

 

 

 

 

 

 

 

 

Spark 3D map(MAP, RPM)

 

3156

3399

4

 

570

659

392800

6590

current cylinder firing time

 

3399

3741

6

1

 

1047

624069

10470

Update TPU

 

3957

4014

 

 

 

57

33975

570

 %cpu usage

 

3741

3819

 

 

 

78

46492

780

 

 

 

 

 

 

 

 

2442034

 

CPU clock (MHz)

16.78

 

 

 

 

 

 

 

 

CPU instruction period (ns)

59.61

 

 

 

 

 

 

 

 

Processing Rate per instruction

10

 

 

 

 

 

 

 

 

FLT_2_INT 5115-5034

81

 

 

 

 

 

 

 

 

INT_2_FLT 5034-4930

104

 

 

 

 

 

 

 

 

              

 

14.1 System Analysis using RapidRMA

Each task was assigned a priority, period and dependency based on the calculations required to complete them.

 

Figure 1.   Task Dependencies in RapidRMA

 

                There are 2 nodes in the 68332 processor.  The TPU runs independently of the CPU so it is modeled as a separate node, Node 0.  The TPU is designed to meet its deadlines regardless of what functions it is programmed to do.  A few tasks that utilize common resources with the CPU were added.  The main CPU is modeled in Node 1.  The base transaction time is 50ns because this is the period of the processor clock at 20 MHz.  All task periods and deadlines are numbers as a function of this base.

Table 3. Task Spreadsheet from RapidRMA

 

Resources for all hardware components were included even though they might not be accessed by any nodes.  Communication between the CPU and TPU was modeled with as an intermodule bus which closely resembles the actual system inside the 68332.  The list of resources can be seen in Table 4.

 


Table 4. Resource Allocation in RapidRMA

 

 

When the analysis is run with rate-monotonic scheduling, it shows that there is an 88.54% processor usage and all of the tasks are schedulable (Figure 2).  The system will remain schedulable with a processing rate as low as 0.89, which means that 10% of the processor usage is available for very low priority tasks.  The serial communications tasks are low priority tasks that were not included.  They are difficult to model because the input is sporadic and the output is periodic.

 


Figure 2.  Schedulability Analysis

14.2 Design and Analysis using Petri-Nets

Petri-Net model for both hardware and software systems of a fuel injector system

 


                This Petri-Net models both hardware and software.   The hard ware requires the software to communicate to the other hardware, therefore it was logical to design a petri net combining both of these systems.  The tasks towards the left-hand side of the petri-net are the hard real-time tasks.  This is shown with the priority 3 transitions.  These tasks occur every 3 ms and are processes before the low and medium priority tasks.  The processing or work times are modeled with delayed transitions.  The tasks are also in logical firing order.  If battery voltage hasn’t been read, then the battery calculations cannot be done.  This is accomplished be using inhibitors at the task selection stage (upon CPU being ready, the highest priority tasks fire first with the only exception being that if a related task has not been processed, then the dependant task can not be chosen until the requirements are satisfied).  The tasks on the right side are done anytime the high priority tasks are not being processed.  However, the lowest 3 tasks are done after all other tasks have been processed.  With the current calculations of processing times, there is more than enough time to do all of the hard and soft real-time tasks within each cycle.  As a bonus, if the petri-net is left to run towards infinity, the soft real-time tasks are processed more often (about 16% over all).  This allows for redundancy, error checking, and/or the adding of additional tasks for increased efficiency within the engine.  This petri-net also has additional firings and places which are not used in the standard petri-net use, but are used instead for statistical analysis.

 

15. Error Analysis

                The main source of error in our calculations is the ‘processing rate per instruction’, which represents the average number of CPU cycles to complete an instruction.  Actual timing of the processor running tasks is needed to estimate this value more accurately.  Certain tasks are also more heavily dependent on complex math functions, which would cause them to have longer computational times than determined.

 

                Because the system was schedulable, we should not have any problems with dropped tasks.  However, the additional task of communicating with a PC was not included.  Timing is important in serial communication, but needs to be run as a low priority task.  Connecting the CPU to a PC should not affect the performance of the system. With the limited resources available, the data transfer may be very slow.  This can be avoided by doing major data transfers while the engine is not running. 

 

16. Conclusions and Future Recommendations

Modeling provides us with a better understanding of the scheduling and timing requirements for an engine management system.  The most difficult part of this project will be determining a method for scheduling tasks without creating overhead.  Certainly the per-cylinder calculations will be completed in sequential order on the actuation of an interrupt from the TPU.  However, the other tasks need to be prioritized so that processor usage can be optimized.  Many of the required values have a very slow response time and only need to be updated every 100 cycles or more.  A real-time interrupt could be used for these but its priority must be lower than that of the per-cylinder calculations.

               

Adding support for positive crankcase pressure (turbocharging or supercharging), would be relatively easy.  The Manifold Absolute Pressure sensor would need to be changed to one that supports a range of positive and negative pressures.  A function is also needed to change the ignition timing and mixture of fuel when positive pressures are detected.

 

The TPU is the most important part of the 68332.  It runs independently and frees many of the real-time requirements of the CPU because it maintains signals to the pin without updating. If the engine were to run statically, without any sensors changing, the CPU could be stopped and the engine would still run.  Since the conception of the EFI332 project over 5 years ago, Motorola has developed faster processors that may be more suitable for this application.  The amount of processing required to do a complete calculation for every cylinder is possible on the 68332, but only for a 4-cylinder engine.  The MPC555 has the same TPU capabilities as the 68332, with a much faster CPU speed (40 MHz).  A real-time operating system, such as RTEMS, may be a viable solution on the MPC555 with the extra processing power available.  RTEMS would provide more control over the task scheduling and priority.

 


Appendix A. References

http://www.apocalypse.org/pub/u/paul/docs/cstyle/cstyle16.htm

http://www.csc.fi/math_topics/Publ/comptrust.html

http://www.manufacturing.net/magazine/dn/archives/1997/dn1006.97/19f8996.htm

http://www.computing.edu.au/~roger/sr_html/node18.html (safety)

http://www.cms.livjm.ac.uk/www/homepage/cmseng/p1.htm (maintainability)

http://www.aln.org/alnweb/journal/issue2/moonen.htm (efficiency)

http://members.edventures.com/terms/e/efficiency/definition.html (efficiency)

http://www.ontek.com/mikey/optimization.html (efficiency)

http://www.cwi.nl/ftp/steven/enquire/enquire.html (portability)

http://www.cigital.com/definitions/testability.html (testability)

http://webster.cs.ucr.edu/Page_softeng/softDevGuide_3.html#section_3.11 (testability)

http://www.ag.auburn.edu/users/gparmer/efi/myfaq.htm (efi site)

http://www-personal.monash.edu.au/~viking/efi/EFI332-FAQ.txt (efi site)

http://www.rtems.com (Real Time Operating System, supported by OarCorp)

http://fiddes.net/coldfire (gnu debugger information)

http://www.mot-sps.com (Motorola semiconductor web site)

http://www.diy-efi.org/efi332 (Home page for hardware development)