1
\$\begingroup\$

I'm designing a MIDI receiver port (using Altera CLPD MAX 7000S). The device samples the second byte it receives and outputs the binary value on an LEDs. The device works fine whenever I input MIDI notes (using MIDI-OX software) at a normal speed. However, whenever I input notes too fast, or input two notes at the same time), it doesn't work correctly (outputs to incorrect LEDs whenever I input any note).

This is my Verilog code. I sample the MIDI bit at the middle.

//using a 4MHz clock using a 31250bit/s baud rate (1 bit -> 32 us; all 10 bits -> 320us  -> 10.3 bits -> 11 bits)
module timer(input clk, input reset, input enable, output [10:0] out);
reg [10:0] timer;
initial timer = 11'b0;
assign out = timer;

always @(posedge clk) begin
    if(!reset)  //if reset is pressed, set timer back to 0
        timer <= 11'b00000000000;
    else if(enable == 1'b1) //if enable is high
            begin
                timer <= timer + 11'b00000000001; //increment counter every time
                if(timer == 11'b11111111111)     //if counter reaches max value (should never reach it)
                    timer <= 11'b00000000000; //set it back to 0
            end
    else
        timer <= 11'b00000000000; // if enable is low, set the timer to 0
end
endmodule


module rx(input clk, input reset, input in, output [7:0] out);

parameter IDLE = 2'b01;
parameter BYTE_CUR = 2'b10;
parameter BYTE_END = 2'b11;
parameter HIGH = 1'b1;
parameter LOW = 1'b0;

reg [7:0] LEDs;
initial LEDs = 8'b0;

reg [7:0] LED;
initial LED = 8'b0;
assign out = LED;


reg [1:0] state;
initial state = IDLE;

reg [2:0] byteNo; 
initial byteNo = 3'b000;

reg timer_en;
initial timer_en = 1'b0;

wire timer_en_wire;
assign timer_en_wire = timer_en;

wire [10:0] timer_wire;

timer t(.clk(clk), .reset(reset), .enable(timer_en_wire), .out(timer_wire)); 

always @(posedge clk) begin
    if(!reset)
    begin
        LEDs <= 8'b0;
        LED <= 8'b0;
        timer_en <= 1'b0;
        state <= IDLE;
        byteNo <= 3'b000;
    end 
    else begin
    if(state == IDLE && in == LOW) // if IDLE and detect a LOW on the input line, start bit has arrived
    begin
        state <= BYTE_CUR;  //change state to BYTE_CUR
        byteNo <= byteNo + 3'b001; // increment byteNo
        timer_en <= 1'b1; // enable the timer
    end
    if(state == BYTE_CUR) //currently a byte
    begin
        case(timer_wire)
            11'b00011000000: LEDs[7] <= in; // 1.5 BT
            11'b00101000000: LEDs[6] <= in; // 2.5 BT
            11'b00111000000: LEDs[5] <= in; // 3.5 BT
            11'b01001000000: LEDs[4] <= in; // 4.5 BT
            11'b01011000000: LEDs[3] <= in; // 5.5 BT
            11'b01101000000: LEDs[2] <= in; // 6.5 BT
            11'b01111000000: LEDs[1] <= in; // 7.5 BT
            11'b10001000000: LEDs[0] <= in; // 8.5 BT
            11'b10100000000: begin   // 10 BT
                state <= BYTE_END;
                timer_en <= 1'b0;
            end
        endcase
    end
    if(state == BYTE_END) //if byte has ended
    begin
        if(byteNo == 3'b010) // if byte number is 2, output the lights
        begin
            LED <= LEDs;
        end
        if(byteNo == 3'b101) // if byte number is 5, clear the output
        begin
            LED <= 3'b000;
        end
        if(byteNo == 3'b110) // if byte number is 6, reset byte counter to 0
        begin
            byteNo <= 3'b000;
        end
        state <= IDLE; //set state back to IDLE
    end
    end
end

endmodule
\$\endgroup\$

closed as too broad by Eugene Sh., RoyC, Lior Bilia, Elliot Alderson, Finbarr Dec 17 '18 at 10:52

Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer. Avoid asking multiple distinct questions at once. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

3
\$\begingroup\$

I see two potential issues.

First of all, your in input is asynchronous to your internal clock, so you must synchronize is before using it in your state machine. The specific problem involves the transition from IDLE (encoded 2'b01) to BYTE_CUR (encoded 2'b10) — this requires both state variables to change, and the asynchronous input could cause them to be evaluated differently, resulting in a transition to state 2'b00 or 2'b11, which is not what you intend.

Second, you have this three-bit counter called byteNo. (BTW, this is also adversely affected by the asynchronous input.) You seem to be assuming that every message is 6 bytes long, and that the note number is the second byte of the message. I'm not sure where you get this — it isn't in any MIDI specification that I've seen.

In general, channel messages are 3 bytes long, comprising a status byte (e.g., "note on") and two data bytes (e.g., note number and velocity). Most MIDI devices will omit the second and subsequent status bytes when sending multiple "note on" messages in rapid succession, which means that you could be looking at a stream of bytes like:

Note On | Note # | Vel. | Note # | Vel. | Note # | Vel. | ...

In other words, you need a much more sophisticated state machine that keeps track of the most recent status byte and then parses the subsequent data bytes accordingly.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.