This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

TM4C1294NCPDT: Writing and Reading to a Register Through the External Peripheral Interface

Part Number: TM4C1294NCPDT

Hello, 

After the last few days of diving into the EPI interface and how to get it functioning with a Zilog Z16C30 Universal Serial Controller i have come to an issue with writing and reading. I have double checked with the manufacturer of the Zilog part on what their correct initialization routine should be based on a previous development kit they produced. However i noticed that the data being sent from the serial controller did not look correct. Thinking that loopback mode might help alleviate some of the concerns i implemented that, although when i read the register that should hold the data that was send i only see the register index returned and no data. Attached is the current configuration of the EPI Driver along with the configuration of the universal serial controller and read/write of data for the serial bus.

Any help would be appreciated,

David

/*
 * EPI.c
 *
 *  Created on: Nov 1, 2019
 *      Author: harrida
 */

#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "inc/hw_epi.h"
#include "inc/hw_gpio.h"
#include "driverlib/debug.h"
#include "driverlib/fpu.h"
#include "driverlib/gpio.h"
#include "driverlib/interrupt.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom.h"
#include "driverlib/rom_map.h"
#include "driverlib/sysctl.h"
#include "driverlib/timer.h"
#include "driverlib/epi.h"
#include "driverlib/udma.h"

#include "Inc/MicrocontrollerDefinitions.h"

#include "Drivers/EPI.h"

static volatile uint16_t *g_pui16EPISdram;

void InitalizeEPIDriver(void)
{
    // RESET pm6
    GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_6, 0);
    SysCtlDelay(10000);
    GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_6, GPIO_PIN_6);
    SysCtlDelay(10000);

    EPIModeSet(EPI0_BASE, EPI_MODE_HB16); //general mode EPI_MODE_GENERAL EPI_MODE_HB16
    EPIDividerSet(EPI0_BASE, 0x0A0A); //1=60MHz, 10=10MHz, 119=1MHz Sets the clock divider for the EPI module�s CS0n/CS1n.
    EPIConfigHB16Set(EPI0_BASE,
    EPI_HB16_MODE_ADMUX | //sets data and address muxed, AD[15:0].
    EPI_HB16_WRWAIT_3 | //sets write wait state to 2 EPI clocks.
    EPI_HB16_RDWAIT_3 | //sets read wait state to 2 EPI clocks.
    EPI_HB16_CLOCK_GATE_IDLE| // holds the EPI clock low when no data is available to read or write.
    EPI_HB16_ALE_LOW| //sets address latch active low.
    EPI_HB16_CSCFG_ALE_DUAL_CS,// sets EPIS030 as an address latch (ALE),
                                //!       EPIS027 as CS0n and EPIS026 as CS1n with the asserted chip select
                                //!       determined from the most significant address bit for the respective
                                //!       external address map.
    1); // the maximum number of external clocks to wait if a FIFO ready signal is
    //holding off the transaction.
    EPIAddressMapSet(EPI0_BASE,
    EPI_ADDR_PER_SIZE_256MB | //256MB location
    EPI_ADDR_PER_BASE_A); //EPI0 is mapped from 0xA0000000 to 0xA00000FF.


    // READ WRITE OPERARION FOR VERIFICATION
    // Write BCR:
    // SepAd = 0 as this is 16 bits wide
    // 16-Bit = 1 for 16-bit operation
    // 2PulseAck = 0 as PITACK is not used
    // SRightA = 1
    g_pui16EPISdram = (uint16_t *)0xA0000000;

    g_pui16EPISdram[0x05] = 0x0005;

    SetRegisterConfiguration(0x80);
    SetRegisterConfiguration(0x00);
}

void GetEPIData(uint16_t value)
{
    uint16_t testA = 0;
    uint16_t testB = 0;
    // Set the EPI memory pointer to the base of EPI memory space. Note that
    // g_pui16EPISdram is declared as volatile so the compiler should not
    // optimize reads out of the memory. With this pointer, the memory space
    // is accessed like a sim*ple array.
    g_pui16EPISdram = (uint16_t *)0xA0000000;
    // READ WRITE OPERARION FOR VERIFICATION
    //g_pui16EPISdram[0 + 0x1C] = 0xFFFF; // MISR
    //g_pui16EPISdram[58] = 2;//value;
    g_pui16EPISdram[48] = 1;//value;

    testB = g_pui16EPISdram[32];
    //g_pui16EPISdram[186] = 2;//value;
    g_pui16EPISdram[176] = 1;//value;

    testA = g_pui16EPISdram[160];
}

void SetRegisterConfiguration(uint8_t RegBase)
{
    if(RegBase != 0 | RegBase != 0x80)
    {
        RegBase = 0x80;
    }
    // Setup for Register
    //Zilog Support
    //
    //---------------------------------------------
    // 2. Initializing the USC to HDLC mode (default)
    //---------------------------------------------

    // BCR
    //> 0x0005 (uses 16-bit data; single pulse ack; multiplexed bus)

    // CCAR
    g_pui16EPISdram[RegBase + 0x00] = 0x0400; //(channel reset bit = 1)
    g_pui16EPISdram[RegBase + 0x00] = 0x0000; //(channel reset bit = 0)
    g_pui16EPISdram[RegBase + 0x00] = 0xB800; //(select D7-0 first)

    // CMR
    g_pui16EPISdram[RegBase + 0x02] = 0x1606; //(Tx/Rx mode HDLC, Tx Under Condition ABORT, No Preamble, Rx AddSearchmode disabled)

    // RMR
    g_pui16EPISdram[RegBase + 0x22] = 0x0600; //(Rx CRC Start = 1, RX CRC Enable = 1)

    // TMR
    g_pui16EPISdram[RegBase + 0x32] = 0x0700; //(Tx CRC Start = 1, Tx CRC Enable = 1 , Tx CRC at End = 1)

    // CMCR
    g_pui16EPISdram[RegBase + 0x10] = 0x0224; //(BRG1 clock source CTR0 output = 0x00, BRG0 clock source RxC pin = 0x10,  DPLLSrc = 0x00, TxCLKSrc = 100, RxCLKSrc = 100

    // IOCR
    g_pui16EPISdram[RegBase + 0x16] = 0x0008; //(TxCMode = Drive /TxC with TxCLK)

    // TC0R
    g_pui16EPISdram[RegBase + 0x2E] = 0x009F; //(divisor value for 115200 baud for BRG0)

    // TCSR
    g_pui16EPISdram[RegBase + 0x34] = 0x6000;
    // TICR
    g_pui16EPISdram[RegBase + 0x36] = 0x0F00;
    // TCSR
    g_pui16EPISdram[RegBase + 0x34] = 0x7000;
    // TICR
    g_pui16EPISdram[RegBase + 0x36] = 0x0F00;
    // TCSR
    g_pui16EPISdram[RegBase + 0x34] = 0x0300;

    // CCSR
    g_pui16EPISdram[RegBase + 0x04] = 0x2000;

    // CCR
    g_pui16EPISdram[RegBase + 0x06] = 0x0080;

    // RCLR
    g_pui16EPISdram[RegBase + 0x2A] = 0xFFFF;



    //---------------------------------------------
    // 3. Enabling the USC
    //---------------------------------------------

    // HCR
    g_pui16EPISdram[RegBase + 0x12] = 0x0001; // BRG0 Enable

    // RMR
    g_pui16EPISdram[RegBase + 0x22] = 0x0602; // Enable Rx  , therefore 0x602

    // TMR
    g_pui16EPISdram[RegBase + 0x32] = 0x0702; // Enable Tx , therefore 0x702

    // (Set Loopback part)

    // CCAR
    g_pui16EPISdram[RegBase + 0x00] = 0x0300; // (Mode Control, IntLocalLoopback)
    // My Investigation
/*    g_pui16EPISdram[RegBase + (0) << 1] = 0x0400; // CCAR
    g_pui16EPISdram[RegBase + (0) << 1] = 0x0000; // CCAR
    g_pui16EPISdram[RegBase + (0) << 1] = 0xB800; // CCAR

    g_pui16EPISdram[RegBase + 0x16] = 0x0F36; // IOCR
    //!
    //! 0xc000
    //! CTS Pin = 0x Active Low
    //!
    //! 0x3000
    //! DCD Pin = 00 Active Low Rx Carrier
    //!
    //! 0x0c00
    //! TxReq = 11 txReq Driven High
    //!
    //! 0x0e00
    //! RxReq = 11 rxReg Driven High
    //!
    //! 0x00c0
    //! TxD = 00 Transmitter Output
    //!
    //! 0x0038
    //! TxC = 110 Output of CTR1
    //!
    //! 0x0007
    //! RxC = 110 Output of CTR0

    g_pui16EPISdram[RegBase + 0x10] = 0xF46C; // CMRC
    //!
    //! 0xc000
    //! CTR1 Source = 11 CTR1 Source is TxC pin
    //!
    //! 0x3000
    //! CTR0 SOurce = 11 CTR0 Source is TxC pin
    //!
    //! 0x0c00
    //! BRG1 Source = 01 BRG1 Source is CTR1
    //!
    //! 0x0300
    //! BRG0 Source = 00 BRG0 Source is CTR0
    //!
    //! 0x00c0
    //! DPLL Source = 01 DPLL Source is BRG1
    //!
    //! 0x0038
    //! TxCLKSrc = 101 BRG1 Ouput
    //!
    //! 0x0007
    //! RxCLKSrc = 100 BRG0 Output

    g_pui16EPISdram[RegBase + 0x2E] = 0x0001; // TC0R

    g_pui16EPISdram[RegBase + 0x3E] = 0x0001; // TC1R

    g_pui16EPISdram[RegBase + 0x12] = 0x50DD; // HCR
    //!
    //! 0xc000
    //! CTR0 Divisor = 01 1/16
    //!
    //! 0x2000
    //! CTR1 Divisor Select = 0 CTR0 Select
    //!
    //! 0x1000
    //! CVOK = 1 Dont Report Violations
    //!
    //! 0x0c00
    //! DPLL Divisor = 00 1/32
    //!
    //! 0x0300
    //! DPLL Mode = 00 Disable
    //!
    //! 0x00c0
    //! TxA Mode = 11 Drive High
    //!
    //! 0x0020
    //! BRG1 Single Cycle = 0 Continuous
    //!
    //! 0x0010
    //! BRG1 Enable = 1
    //!
    //! 0x000C
    //! RxA Mode = 11 Drive High
    //!
    //! 0x0002
    //! BRG0 Single Cycle = 0 Continuous
    //!
    //! 0x0001
    //! BRG0 Enable = 1

    g_pui16EPISdram[RegBase + 0x02] = 0x0626; // CMR
    //!
    //! 0xF000
    //! TxSubMode
    //!
    //! 0x0F00
    //! TxMode = 0110 HDLC Mode
    //!
    //! 0x00F0
    //! RxSubMode = 0010 1 Byte Address, 1 Control Byte
    //!
    //! 0x000F
    //! RxMode = 0110 HDLC Mode
    //!

    g_pui16EPISdram[RegBase + 0x04] = 0x2000; // CCSR
    //!
    //! 0x8000
    //! RCC FIFO Overflow = 0
    //!
    //! 0x4000
    //! RCC FIFO Not Empty = 0
    //!
    //! 0x2000
    //! RCC Clear = 1
    //!
    //! 0x1000
    //! DPLL In Sync
    //!
    //! 0x0800
    //! DPLL Miss 2 Clocks
    //!
    //! 0x0400
    //! DPLL Miss CLock
    //!
    //! 0x0300
    //! DPLL Edge  = 0
    //!
    //! 0x0080
    //! HDLC In Loop
    //!
    //! 0x0040
    //! HDLC Transmitting Loop
    //!
    //! 0x001F
    //! Tx Residue = 000 Last HDLC Frame Contains 8 bits
    //!

    g_pui16EPISdram[RegBase + 0x06] = 0x1540; // CCR
    //!
    //! 0xC000
    //! Tx Control BLocks = 00 no use
    //!
    //! 0x2000
    //! Wait For Tx Trigger = 0
    //!
    //! 0x1000
    //! Send Preamble flag = 1 send flag
    //!
    //! 0x0C00
    //! Preamble = 01 16 bit Preamble
    //!
    //! 0x0300
    //! Tx Preamble = 01 Preamble = all 1's
    //!
    //! 0x00C0
    //! Rx Status Vlocks = 01 16 Bit
    //!
    //! 0x0020
    //! Wait for Rx Trigger = 0
    //!
    g_pui16EPISdram[RegBase + 0x1C] = 0x8080; // DCCR
    //!
    //! 0xC000
    //! Clear IUS Bits 10
    //!
    //! 0x00C0
    //! Clear IP Bits 10
    //!

    g_pui16EPISdram[RegBase + 0x18] = 0x7E80; // ICR
    //!
    //! 0x8000
    //! MIE enable Interrupts = 0
    //!
    //! 0x4000
    //! Disable DLC Interrupt Enable Out = 1
    //!
    //! 0x2000
    //! IntACK Cycle Vector = 1 Do not return
    //!
    //! 0x1E00
    //! Interrupt Vectors = 1111 do not include status
    //!
    //! 0x00C0
    //! INterrupt Operation = 10 - clear interrupts

    g_pui16EPISdram[RegBase + 0x1C] = 0xAAA0; // MISR
    //!
    //! 0x8000
    //! Rx CL/U = 1 Open Latches for RxC
    //!
    //! 0x4000
    //! RxC value
    //!
    //! 0x2000
    //! TxCL/U = 1 Open Latches for TxC
    //!
    //! 0x1000
    //! TxC Value
    //!
    //! 0x0800
    //! RxRl/U = 1 Open Latch for RxR
    //!
    //! 0x0400
    //! RxR Value
    //!
    //! 0x0200
    //! TxRL/U = 1 Open Latch for TxR
    //!
    //! 0x0100
    //! TxR Value
    //!
    //! 0x0080
    //! DCDL/U = 1 Open Latch for DcD
    //!
    //! 0x0040
    //! DCD Value
    //!
    //! 0x0020
    //! CTSL/U = 1 Open Latch for CTS
    //!
    //! 0x0010
    //! CTS Value

    g_pui16EPISdram[RegBase + 0x2A] = 0xFFFF; // RCLR
    //!
    //! Enable RCC no max frame/message
    //!
    g_pui16EPISdram[RegBase + 0x26] = 0x0000; // RICR

    g_pui16EPISdram[RegBase + 0x34] = 0x0500; // TCSR

    g_pui16EPISdram[RegBase + 0x3A] = 0x0000; // TCLR

    g_pui16EPISdram[RegBase + 0x36] = 0x0000; // TICR

    g_pui16EPISdram[RegBase + 0x22] = 0x0602; // RMR

    g_pui16EPISdram[RegBase + 0x32] = 0x0602; // TMR
*/

    /*g_pui16EPISdram[RegBase + 0] = 0x05;
    g_pui16EPISdram[RegBase + (1) << 1] = 0x0606;
    g_pui16EPISdram[RegBase + (2) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (3) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (4) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (5) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (6) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (7) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (8) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (9) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (10) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (11) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (12) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (13) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (14) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (15) << 1] = 0x05;
    // 16 is send/received
    g_pui16EPISdram[RegBase + (17) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (18) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (19) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (20) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (21) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (22) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (23) << 1] = 0x05;
    // 24 is send/receive
    g_pui16EPISdram[RegBase + (25) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (26) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (27) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (28) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (29) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (30) << 1] = 0x05;
    g_pui16EPISdram[RegBase + (31) << 1] = 0x05;*/
}

  • Below is a sample waveform:

    At 7.9 Seconds the software selects address 0x30 for channel B to send the two bytes of data of value 0x0001. Then at 8.2 seconds the software selects register 0x20 to read the data that was transmitted through the transmit loopback. However the value that it reads is 0x20. At 8.4 seconds the software selects register 0xB0  for channel A to send two bytes of data with a value of 0x001. Then at 8.7 seconds the software reads 0xA0 which would be the recieve register for channel A however the value read is 0xA0. 

    David

  • Hello David,

    The loopback mode would be specific to that device right? That wouldn't be a TM4C controlled element?

    I've read through your post a few times today and I'm still not really sure guidance to offer here still. Right now it is not clear to me what question specifically about the EPI interface I could provide help with. If the issue is purely with the correct interfacing of the device, then that would be application specific unless you feel the TM4C is not sending out the correct bytes while using the TivaWare API's.

  • Hello Ralph, 

    You are correct the loop back mode is something that would be controlled by the external device and not TI. 

    I apologize for the lack of direction on my question above. I wanted to double check that my configuration of the EPI driver is good along with verifying that i am accessing the EPI peripheral to send and receive data correctly. Because there is very little documentation provided by TI on how to access the EPI interface and the only example is for a SRAM component it was difficult to verify. Once i have verified that the EPI interface is functioning correctly that tells me there is still an issue on the external device where something is not configured correctly. Im just trying to figure out where the breakdown is in communication. 

    David

  • Greetings,

    Staff/I 'Feel your pain' - yet must ask, as these ARM MCUs are 'UART Rich' - what is the appeal of this (pardon) 'beyond Long in the Tooth' Serial Controller?   (only its (possible) ability to 'Run multiple Serial Ports simultaneously' - dawns to my small group.)

    We sought the "Z16C30" datasheet - neither Zilog, nor Digikey nor Mouser - responded w/other than a 'Blank Screen.'   (It is well known that 'Z' has seen/enjoyed 'better days.')    In the (very) distant past [1980-90s] several staffers & I used (first Rom-Less) & then Flash-Based Zilog MCUs.    (never the 'famed' Z80.)    As 'Z' often followed Intel - it is suspected that Intel (or a licensee) may have produced a similar device - which perhaps aids your effort...     And ... 'Exiting our DeLorean' - would not an 'FPGA' rather well -  modernize & fulfill such role?

    Not knowing the chip's interface - 'KISS' (may) suggest that you (at least temporarily) interface via the combination of several of your MCU's Parallel Ports - reserving one of those ports for 'Command/Control  'Bit' Signal Generation.'    It should be possible for you to 'properly/quickly' switch the data port from 'Write to Read' - via this vendor's (comprehensive) API.    Again - this should be faster/easier than, 'Mastering the EPI Exchange' - as you've already - painfully noted.  

    Perhaps something here proves useful ... and we really would like to learn the 'basis' for your use of this, 'Serial Controller' - especially as 'such ports are plentiful' - and well described' - w/in your MCU...

  • Hello CB1,

    I do agree that the ports on the TI products are well described and plentiful examples are provided within the Tivaware Library. However the use of this external Serial controller is from the customer's design and uses an HDLC Serial protocol which is needed for their end device.

    Unfortunately that is all the reasoning i was given on using the external serial controller. Throughout this week that I've been working on trying to get things up and running for the serial controller, I have thought about using the onboard serial devices and writing/finding and example of the HDLC Serial protocol and removing the external serial controller completely. At this point that may be my only option due to the lack of helpful response from the manufacturer of the serial controller.

    -David

  • Hello David,

    Terrific - quick & detailed response - much appreciated - thank you.

    That has happened to my small firm as well - always 'Nasty' - and (too often) the client 'Fails to recognize nor appreciate the (sometimes) massive time & effort - required for their Rescue!'    

    That said - several suggestions follow:

    • Might you obtain (even) 'One Working System' - which includes the (original) MCU which drives the 'Z16C30?'    Armed w/this - you may, 'Probe & Capture the key signal activity' - which (again sometimes) serves as an (almost) datasheet.    (but for the 'devil' Timing Specs!)
    • Staff employed 'Google' to search for 'HDLC' - references are plentiful - yet, 'Much New Learning' lands upon you.
    • It is wondered - if any of the newer communication protocols - 'borrowed' (likely stole) from HDLC?    I know in our firm's review of USB 3.0 - past & powerful methods - (while not (always) identified as such) appeared several times.    I believe that 'Such a search' may 'Speed & Ease' your effort.
    • And again - the past mention of 'Intel and/or others' - also producing such 'HDLC' ICs - may provide data which 'overlaps and/or otherwise serves your learning efforts!'     (My group highly doubts that 'Z' alone - produced the 'only HDLC Capable' device.)
    • Earlier you noted the presence of an Eval Board - holding the Z16C30.   You 'MUST' acquire one - that seems the most eased & shortest path for your 'HDLC Data Harvest!'

    Should each/every of the above listing 'Fail' - I return to the suggestion of, 'Switching to multiple parallel, MCU Ports connecting to your Z16C30.   

    Surely 'Z16C30' is a '5V' I/O device - is it possible that the '3V3' levels produced by your MCU - have 'truly & effectively' - been 'captured by the Z16C30?    It may even be that a 'Single Command/Control BIT'  (i.e. VIH) - requires  ≥80% of the Z16C30's VDD - which your MCU cannot produce directly!    (Such could prove KEY - is that not so?)

  • Hello David,

    David Harris said:
    I wanted to double check that my configuration of the EPI driver is good along with verifying that i am accessing the EPI peripheral to send and receive data correctly.

    Well the tricky part there is how much of the configuration is specific to the device.

    Like for example, I see you are using HB16 mode but I am not really certain if that is the ideal option here. If its too constrained, you may need to use General-purpose mode instead. Host-Bus mode generally is used for memory interfaces though so there could be some issue with using it for this USC.

    I also am not quite sure about the setting for EPIDividerSet - that is a pretty large divider and would come out to... 46693 Hz if using 120 MHz?

    Beyond that the read/writes seem fine to me.

  • Hello David,

    As I haven't heard back from you, I’m assuming you were able to make progress with your issue.

    I am going to close this thread on our end now, but if you need to bounce around further ideas on how to interface the USC, feel free to post another reply (or open a new thread if the thread has locked due to time-out).

  • Hello Ralph, 

    Due to some of the timing constraints i was seeing when using the EPI module i have decided to switch over to a GPIO implementation. I wanted to thank everyone for their support.

    David

  • cb1_mobile said:
    Should each/every of the above listing 'Fail' - I return to the suggestion of, 'Switching to multiple parallel, MCU Ports connecting to your Z16C30.   

    Good to note your agreement.    (May it be noted that, 'NOT Everyone' offered up this 'GPIO Path' - which you have now adopted...)

    So often - it has proven that, 'Simple, Direct & Well Known/Established' proves the, 'Superior Driver to Design Success!'     And perhaps 'later' - after you've succeeded -  you (then) may better measure & chart your signal timings & levels - and  have another 'GO' w/the EPI.

    And - to that end - you may consider:

    cb1_mobile said:
    Surely 'Z16C30' is a '5V' I/O device - is it possible that the '3V3' levels produced by your MCU - have 'truly & effectively' - been 'captured by the Z16C30?    It may even be that a 'Single Command/Control BIT'  (i.e. VIH) - requires  ≥80% of the Z16C30's VDD - which your MCU cannot produce directly!    (Such could prove KEY - is that not so?)

    Best of luck - again if you can 'acquire' that past Zilog Eval Board (make client buy it) your effort would be much enhanced...