§ Программный код
Модуль принимает сигналы, опорная частота 25 мгц. Может принимать как ASCII-код, так и AT.module keyboard ( // 25 MHZ input clock, input reset_n, // Рассылка команд input cmd, // =1 строб команды input [7:0] dat, // Отсылаемый байт // Ввод-вывод на клавиатуру и наоборот inout ps_clk, inout ps_dat, // Исходящие данные от клавиатуры output reg [7:0] kbd, // Данные от клавиатуры output reg hit, // =1 Одиночный хит на получение данных от контроллера output reg kdone, // =1 Если пришел ASCII ответ output reg [7:0] ascii, output reg err, // =1 Ошибка обработки или таймаут output ready // =1 Устройство готово к приему команды cmd=1, dat ); // ----------------------------------------------------------------------------- localparam PERIOD = 124, // 200 kHz (25000000/200000 - 1) CWAIT = 20; // Ожидание перед отправкой команды x 5 мкс localparam IDLE = 0, RECEIVE = 1, TRANSMIT = 2; // ----------------------------------------------------------------------------- assign ready = (CMD == 0); assign ps_clk = we_clk ? PS_CLK : 1'bz; assign ps_dat = we_dat ? PS_DAT : 1'bz; // ----------------------------------------------------------------------------- // 200 kHz делитель частоты (5 мкс один отсчет) reg [ 1:0] stage; reg [ 7:0] t; reg [ 9:0] dm; // Отсчет таймаута 5 мк x 1024 = 5 ms reg [ 6:0] dx; // 200 kHz reg [ 1:0] rt; // Обнаружение CLOCK reg [ 3:0] cnt; reg CMD; reg [ 9:0] DAT; reg we_clk, we_dat; reg PS_CLK, PS_DAT; // ----------------------------------------------------------------------------- wire valid = ps_dat ^ (^kbd); reg released, extended, shift; // ----------------------------------------------------------------------------- always @(negedge clock) if (reset_n == 0) begin t <= 0; dx <= 0; dm <= 0; we_clk <= 0; we_dat <= 0; cnt <= 0; err <= 0; stage <= IDLE; CMD <= 0; DAT <= 8'h00; kdone <= 1'b0; shift <= 1'b0; released <= 1'b0; extended <= 1'b0; end else begin hit <= 1'b0; kdone <= 1'b0; // Регистрация команды для отсылки устройству if (cmd) begin CMD <= 1; DAT <= {/*STOP*/ 1'b1, /*PAR*/ ~^dat, dat}; err <= 0; end // 5 микросекунд, 200 kHz (основано на 25 Mhz) 25M / 125 = 200k if (dx == PERIOD) begin // Регистрация сигнала CLK от клавиатуры rt <= {rt[0], ps_clk}; // Проверка на таймаут, сброс в IDLE при ошибке if (stage) begin dm <= dm + 1; if (&dm) begin stage <= IDLE; CMD <= 0; err <= 1; end end case (stage) // Ожидание команды от хоста или данных от клавиатуры/мыши // ----------------------------------------------------------------- IDLE: begin t <= 0; cnt <= 0; // [R] Получен сигнал от клавиатуры if (rt == 2'b10) begin stage <= RECEIVE; err <= 0; end // [T] Запрос исполнения команды else if (CMD) begin stage <= TRANSMIT; err <= 0; {we_clk, we_dat} <= 2'b11; {PS_CLK, PS_DAT} <= 2'b11; end end // Прием сигнала от устройства // https://ru.wikipedia.org/wiki/Скан-код // ----------------------------------------------------------------- RECEIVE: if (rt == 2'b01) begin t <= t + 1; dm <= 0; case (t) // Старт-бит должен быть равен 0, иначе не принимать данные 0: if (ps_dat) begin stage <= IDLE; err <= 1; end // Прием LSB (8 бит) 1,2,3,4,5,6,7,8: kbd <= {ps_dat, kbd[7:1]}; // Сверка бита четности: // - Если количество ЧЕТНО, то там будет 0, а в PS_DAT=1, так что ОК // - И наоборот также, НЕЧЕТНО дает 1, а в PS_DAT=0, и это OK // - Иначе не OK ни разу 9: begin hit <= valid; // Клавиша отпущена if (kbd == 8'hF0) begin released <= 1'b1; end else if (kbd == 8'hE0) begin extended <= 1'b1; end // Декодирование клавиши else begin // Отправка клавиши только при KeyDown kdone <= valid && !released; // Декодирование клавиши case ({extended, kbd}) // Левый и правый SHIFT равнозначны /* SH */ 8'h12, 8'h59: begin shift <= ~released; kdone <= 1'b0; end // Цифробуквенная клавиатура /* Aa */ 8'h1C: ascii <= shift ? 8'h41 : 8'h61; /* Bb */ 8'h32: ascii <= shift ? 8'h42 : 8'h62; /* Cc */ 8'h21: ascii <= shift ? 8'h43 : 8'h63; /* Dd */ 8'h23: ascii <= shift ? 8'h44 : 8'h64; /* Ee */ 8'h24: ascii <= shift ? 8'h45 : 8'h65; /* Ff */ 8'h2B: ascii <= shift ? 8'h46 : 8'h66; /* Gg */ 8'h34: ascii <= shift ? 8'h47 : 8'h67; /* Hh */ 8'h33: ascii <= shift ? 8'h48 : 8'h68; /* Ii */ 8'h43: ascii <= shift ? 8'h49 : 8'h69; /* Jj */ 8'h3B: ascii <= shift ? 8'h4A : 8'h6A; /* Kk */ 8'h42: ascii <= shift ? 8'h4B : 8'h6B; /* Ll */ 8'h4B: ascii <= shift ? 8'h4C : 8'h6C; /* Mm */ 8'h3A: ascii <= shift ? 8'h4D : 8'h6D; /* Nn */ 8'h31: ascii <= shift ? 8'h4E : 8'h6E; /* Oo */ 8'h44: ascii <= shift ? 8'h4F : 8'h6F; /* Pp */ 8'h4D: ascii <= shift ? 8'h50 : 8'h70; /* Qq */ 8'h15: ascii <= shift ? 8'h51 : 8'h71; /* Rr */ 8'h2D: ascii <= shift ? 8'h52 : 8'h72; /* Ss */ 8'h1B: ascii <= shift ? 8'h53 : 8'h73; /* Tt */ 8'h2C: ascii <= shift ? 8'h54 : 8'h74; /* Uu */ 8'h3C: ascii <= shift ? 8'h55 : 8'h75; /* Vv */ 8'h2A: ascii <= shift ? 8'h56 : 8'h76; /* Ww */ 8'h1D: ascii <= shift ? 8'h57 : 8'h77; /* Xx */ 8'h22: ascii <= shift ? 8'h58 : 8'h78; /* Yy */ 8'h35: ascii <= shift ? 8'h59 : 8'h79; /* Zz */ 8'h1A: ascii <= shift ? 8'h5A : 8'h7A; // Цифры /* 0) */ 8'h45: ascii <= shift ? 8'h29 : 8'h30; /* 1! */ 8'h16: ascii <= shift ? 8'h21 : 8'h31; /* 2@ */ 8'h1E: ascii <= shift ? 8'h40 : 8'h32; /* 3# */ 8'h26: ascii <= shift ? 8'h23 : 8'h33; /* 4$ */ 8'h25: ascii <= shift ? 8'h24 : 8'h34; /* 5% */ 8'h2E: ascii <= shift ? 8'h25 : 8'h35; /* 6^ */ 8'h36: ascii <= shift ? 8'h5E : 8'h36; /* 7& */ 8'h3D: ascii <= shift ? 8'h26 : 8'h37; /* 8* */ 8'h3E: ascii <= shift ? 8'h2A : 8'h38; /* 9( */ 8'h46: ascii <= shift ? 8'h28 : 8'h39; // Спецсимволы /* `~ */ 8'h0E: ascii <= shift ? 8'h7E : 8'h60; /* -_ */ 8'h4E: ascii <= shift ? 8'h5F : 8'h2D; /* =+ */ 8'h55: ascii <= shift ? 8'h2B : 8'h3D; /* \| */ 8'h5D: ascii <= shift ? 8'h7C : 8'h5C; /* [{ */ 8'h54: ascii <= shift ? 8'h7B : 8'h5B; /* ]} */ 8'h5B: ascii <= shift ? 8'h7D : 8'h5D; /* ;: */ 8'h4C: ascii <= shift ? 8'h3A : 8'h3B; /* '" */ 8'h52: ascii <= shift ? 8'h22 : 8'h27; /* ,< */ 8'h41: ascii <= shift ? 8'h3C : 8'h2C; /* .> */ 8'h49: ascii <= shift ? 8'h3E : 8'h2E; /* /? */ 8'h4A: ascii <= shift ? 8'h3F : 8'h2F; // Разные клавиши /* BACK */ 8'h66: ascii <= 8'h08; /* TAB */ 8'h0D: ascii <= 8'h09; /* ENT */ 8'h5A: ascii <= 8'h0A; /* ESC */ 8'h76: ascii <= 8'h1B; /* SPC */ 8'h29: ascii <= 8'h20; // Дополненный набор /* PGUP */ 9'h17D: ascii <= 8'h01; /* PGDN */ 9'h17A: ascii <= 8'h02; /* UP */ 9'h175: ascii <= 8'h03; /* RT */ 9'h174: ascii <= 8'h04; /* DN */ 9'h172: ascii <= 8'h05; /* LF */ 9'h16B: ascii <= 8'h06; /* DEL */ 9'h171: ascii <= 8'h07; /* INS */ 9'h16C: ascii <= 8'h0B; /* HOME */ 9'h170: ascii <= 8'h0C; /* END */ 9'h169: ascii <= 8'h0D; // Клавиши, которые не играют никакой роли default: kdone <= 1'b0; endcase released <= 1'b0; extended <= 1'b0; end end // Стоповый бит если 0, то это ошибка 10: begin CMD <= 0; stage <= IDLE; err <= ~ps_dat; end endcase end // Процедура отсылки команды на клавиатуру // https://wiki.osdev.org/PS/2_Keyboard#Command_Queue_and_State_Machine // ----------------------------------------------------------------- TRANSMIT: begin t <= t + 1; case (t) // Пролог CWAIT: begin PS_CLK <= 0; end // CLK=0 на 100 мк CWAIT+20: begin PS_DAT <= 0; end // DAT=0 на 50 мк CWAIT+28: begin PS_CLK <= 1; end // CLK=1 старт бит CWAIT+29: begin we_clk <= 0; dm <= 0; end // Ожидание 10 битов от клавиатуры // Ожидание может длиться до 5 миллисекунд, иначе сброс CWAIT+30: begin t <= CWAIT+30; // На негативном фронте CLK отправка данных if (rt == 2'b10) begin PS_DAT <= DAT[0]; DAT <= DAT[9:1]; cnt <= cnt + 1; dm <= 0; end // Отосланы все 10 бит, на позитивном фронте CLK else if (rt == 2'b01 && cnt == 10) t <= CWAIT+31; end // Ждать бита ACK от клавиатуры CWAIT+31: we_dat <= 0; CWAIT+33: begin dm <= 0; t <= CWAIT + (rt == 2'b01 ? 34 : 33); end // Через 10 мкс завершить отсылку и перейти к стадии приема ответа от клавиатуры CWAIT+34: t <= (rt == 2'b10) ? CWAIT+35 : CWAIT+34; CWAIT+35: begin stage <= RECEIVE; t <= 0; end endcase end endcase end dx <= (dx == PERIOD) ? 0 : dx + 1; end endmodule
17 июл 2025, 21:19
© 2011-2025 Разрешил фиговый блин