Starter Question on VHDL and Opinion

Discussion in 'VHDL' started by Brad Smallridge, Jul 6, 2003.

  1. Hello comp.lang.vhdl,

    I am designing a specialized SDRAM controller and have a question about the
    best way to set up some timing signals. I generally have a good idea about
    what signals have to issued at certain clock times. I set up a rather long
    clock cycle, separating the sync from the combo logic like this:

    p1: process (fastclk3)
    if (fastclk3'event and fastclk3 = '1') then
    fastclkcnt <= fastclkcntnext;
    mras <= mrasnext;
    .... and so on

    p2: process (fastclkcnt)

    if ( fastclkcnt = 50 ) then
    fastclkcntnext <= "011110"; --30
    fastclkcntnext <= fastclkcnt + 1;
    end if;
    fastclkcntnext <= "000000";

    And I was going to finish off with the outputs like this

    if (fastclkcnt = 2 ) then -- PRECHARGE
    mrasnext <= '0';
    mcasnext <= '1';
    mwenext <= '0';
    mdqmnext <= '1';
    elsif (fastclkcnt = 4 ) then -- REFRESH
    mrasnext <= '0';
    mcasnext <= '1';
    mwenext <= '0';
    mdqmnext <= '1';
    elsif (fastclkcnt = 12 ) then -- REFRESH
    mrasnext <= '0';
    mcasnext <= '1';
    mwenext <= '0';
    mdqmnext <= '1';

    .... and so on, and quite a few I might add

    BUT one author I was reading stated that evertime I use a <= I am generating
    another driver for the signal and that I should be doing something like

    mrasnext <= '0' when fastclkcnt = 2
    '1' when fstclkcnt =4
    '1' when fastclkcnt =12

    Sort of grouping each output into one statement.
    And but, what a drag, I have to re-figure the outputs at each clock every
    time I play with a timing change.

    Can somebody give me a strategy for 1) producing efficient firmware and 2)
    looks clean and 3) can be changed easily.

    Thanks in advance.

    Brad Smallridge, Jul 6, 2003
    1. Advertisements

  2. It's probably a good idea to provide asynchronous reset on
    this kind of process, so that everything is initialised
    in a completely reliable way.
    Some ideas for tidying that up, later in this message.

    Yup. Either you read a duff book, or you misinterpreted it.

    You DON'T get a new driver for every <= assignment. You
    get a new driver for every process that assigns to the signal.
    Multiple processes, multiple drivers. One process, one driver.

    So, within a process, you can make as many assignments to a
    given signal as you wish, and you get only one driver.
    Even better, if you make multiple assignments to the signal
    in a single pass through the process (no time delay), then
    the last-executed assignment is the only one that does

    process (blah)
    foo <= '0'; --- default, correct in most cases
    if horrible_set_of_conditions then
    foo <= '1'; --- overrides default in this case

    The only possible confusion here is: what is meant by "a process"?
    Obviously an explicit process...begin...end process is indeed
    a process. But you also get a process from a simple concurrent
    signal assignment at the top level of an architecture:

    architecture doohickey of dingbat is
    signal S: std_logic;
    S <= A and B; -- one driver
    S <= P or Q; -- another driver
    process (N, P)
    S <= '0'; -- a third driver...
    if P = '1' then
    S <= not N; -- still the third driver

    There are a few other things that can create processes, but
    what I've said above is a reasonable start.
    Now that's a MUCH tougher question :)

    Broadly I think you're on the right track, but you may find
    that it's better to implement your memory controller as a
    classical state machine rather than decoding everything off
    a counter. You'll no doubt find plenty of examples in
    various books. If your "fast clock" is so fast that you
    need to spend many cycles in a given state, it can be
    a good idea to create a small auxiliary counter. Here's
    an example. There are some declarations missing, but
    I hope you get the general idea. You need to
    "use ieee.numeric_std.all;" to get the "unsigned" data
    type for your counter. This state machine generates
    cycles for an entirely fictitious memory device which
    requires MemEnable to be asserted for many clocks
    before and after the single-clock MemStrobe pulse.

    architecture ....
    type state_type is (Idle, Precharge, Strobe, Recovery);
    signal state: state_type;
    signal count: unsigned(3 downto 0); -- 4-bit aux counter
    process (clock, reset)
    if reset = '1' then
    -- Reset everything.
    state <= Idle;
    count <= (others => '0');
    MemEnable <= '0';
    MemStrobe <= '1';
    elsif rising_edge(clock) then
    -- Clocked logic
    count <= count - 1;
    case state is
    when Idle =>
    if Start = '1' then
    -- Go into the Precharge state.
    state <= Precharge;
    MemEnable <= '1';
    count <= "0011";
    end if; -- Otherwise, stay in Idle
    when Precharge =>
    if count = 0 then
    -- Go into Strobe
    state <= Strobe;
    MemStrobe <= '1';
    end if;
    when Strobe =>
    -- This state lasts only one clock
    MemStrobe <= '0';
    state <= Recovery;
    count <= "1001";
    when Recovery =>
    if count = 0 then
    state <= idle;
    MemEnable <= '0';
    end if;
    end case;
    end if;
    end process;

    Now, I've probably stirred a bit of a hornet's nest here,
    because different folk have widely different opinions
    about how to do these kinds of thing. Note, for example,
    that I've allowed the timeout downcounter to decrement on
    every cycle even when it's not in use. I did that because
    it centralises the decrement operation in one line of code
    (near the top of the process) so the synthesis tool has
    no excuse for failing to recognise it as a counter. Some
    people prefer to embed the decrement operations in the
    relevant places in the code, and accept the risk that
    synthesis may not optimise it so well (although in practice
    most modern tools are pretty good with that kind of thing).
    The obvious alternative is to put the counter in a
    separate block of logic and pass "count enable" and "load
    enable" signals to it from the state machine process;
    personally I find that's a bit clunky, but others think
    it's clear and maps well on to hardware.

    Note too that I have used a single process, rather than
    splitting the state machine into clocked registers and
    asynchronous next-state logic. Personally I feel pretty
    strongly about this, because IMO it makes the code much
    cleaner and saves no end of worry about latches and suchlike,
    and it means that I need only set up signals in situations
    when they change (because they're all held in flip-flops).
    Some people stick with two-process style, claiming it
    improves the optimisation, or better fits their mental model
    of a state machine. Take your pick.

    Finally, if I were coding this myself I would probably have
    used variables rather than signals for the state and count,
    but as you are just getting to grips with VHDL I thought it
    was probably best to keep things straightforward. Some
    writers and trainers advise that you always avoid variables
    in synthesisable logic; I strongly disagree, but it's
    definitely true that using variables requires a bit more
    understanding of what's going on.

    Jonathan Bromley, Consultant

    DOULOS - Developing Design Know-how
    VHDL * Verilog * SystemC * Perl * Tcl/Tk * Verification * Project Services

    Doulos Ltd. Church Hatch, 22 Market Place, Ringwood, Hampshire, BH24 1AW, UK
    Tel: +44 (0)1425 471223 mail:
    Fax: +44 (0)1425 471573 Web:

    The contents of this message may contain personal views which
    are not the views of Doulos Ltd., unless specifically stated.
    Jonathan Bromley, Jul 7, 2003
    1. Advertisements

  3. Thanks Jonathan. That was really helpful for me because I had read about
    state machine design, thought that it might be useful for my design, but had
    not been able to grasp exactly how the code would look. Your example code
    made it crystal clear on how I should proceed. I don't see the VHDL
    solutions right away since I am a VHDL beginner and I use to design in
    schematic capture using mostly moving hot one design. Your code idea looks
    cleaner and it does seem that the hardware may be significantly reduced
    since the counter is much smaller.

    I haven't coded it yet, however. I got the SDRAM working last night and
    decide to play with it a bit before I try different design approaches. I
    also in the future would like to have several states interleaved in the
    cycle. Several Reads for example. SDRAM can do that to different banks. I
    suppose with your design I should set up a state for each bank? Or perhaps
    there is a cleaner way to do that as well.

    Your opinion about the separation of combinatorial logic and registers is
    one that I am still looking for an answer. To tell you the truth, I
    originally coded the SDRAM without such separation, prefering it since it
    was more consise. But when it didn't seem to work right away I then
    separated. It started working later, do to some other problems that I fixed,
    so I don't know if the separation helped or not. Perhaps the documentation
    of the design would be better for one type or the other, I don't know. I am
    using a Cypress 39K if that helps anyone out there give me an idea.

    Thanks to for clearing up the concept of drivers, at least, within one
    process. As a beginner I still don't understand what a process entails.
    Should one have a lot of smaller processes? Should I have a process for each
    output like one book seem to suggest. Very confusing. I wish they would
    invent a language where everybody was forced to be on the same page. VHDL
    seems to be the antithesis of this idea. What a nightmare it must be to
    write the compiler, er, synthesizer? for this language.

    Brad Smallridge
    Brad Smallridge, Jul 9, 2003
  4. Jonathan's suggestions are excellent, I just want to add a minor style

    Instead of

    the output assignments can be condensed as follows...

    -- within a process, only one driver for mctrl is created
    mctrl <= "1111"; -- default case
    if (fastclkcnt = 2 ) then -- PRECHARGE
    -- if (state = PRECHARGE ) then -- cleaner version
    mctrl <= "0101";
    elsif (fastclkcnt = 4 ) then -- REFRESH
    mctrl <= "0101";
    elsif (fastclkcnt = 12 ) then -- REFRESH
    mctrl <= "0101";

    Outside the process, separate parallel assignments are performed only

    mrasnext <= mctrl(3);
    mcasnext <= mctrl(2);
    mwenext <= mctrl(1);
    mdqmnext <= mctrl(0);

    This can make the state machine tidier and easier to follow.
    (Since mctrl has to be visible outside the process, it is a signal,
    e.g. std_logic_vector)

    - Brian
    Brian Drummond, Jul 9, 2003
  5. That's a good idea too. Thanks.
    Brad Smallridge, Jul 11, 2003
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.