We commonly assume a sharp distinction between computer hardware and software: the digital circuit designer takes logic gates as primitives to build processors, while the programmer takes processors as primitives to build applications. Specialists in one of these domains seldom cross into the other. For many years this division of labour has sufficed. The increased circuit density that resulted from improved chip manufacturing has enabled circuit designers to produce faster processors, and everyone benefited. That era has now ended, and the current trend is toward multiprocessors rather than bigger processors. But there is an alternative way to exploit all those extra transistors that are now available: you can use them to provide special assistance to ordinary processors, or even to design circuits that solve problems directly. That approach is often unreasonable, because traditional processors solve many problems efficiently. There are, however, some applications where it does make sense to combine circuit design with programming. Two examples are specialised functional units and data parallel processors. There has been progress in languages and software tools to support such hybrid systems as well as algorithms that make the most of their capabilities. Combining hardware with software also raises some intriguing new questions in computational complexity.